/*
 * Copyright 2026 Oleg Borodin <onborodin@gmail.com>
 */
package storage

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"

	"mstore/pkg/auxuuid"
)

const (
	sha256prefix = "sha256:"
	fileSubdir   = "files"
	tmpSubdir    = "tmps"
)

func (store *Storage) makeCollecionpath(collection string) string {
	return filepath.Join(store.basepath, fileSubdir, collection)
}

func (store *Storage) makeFilepath(collection, filename string) string {
	return filepath.Join(store.basepath, fileSubdir, collection, filename)
}

func (store *Storage) makeTmppath(tmpName string) string {
	return filepath.Join(store.basepath, tmpSubdir, tmpName)
}

func (store *Storage) makeTmpsubdir() string {
	return filepath.Join(store.basepath, tmpSubdir)
}

func (store *Storage) makeFilesubdir(collection, filename string) string {
	return filepath.Join(store.basepath, fileSubdir)
}

func (store *Storage) FileExists(collection, filename string) (bool, int64, error) {
	var err error
	var exists bool
	var size int64
	filename = store.makeFilepath(collection, filename)
	stat, err := os.Stat(filename)
	if os.IsNotExist(err) {
		err = nil
		return exists, size, err
	}
	if err != nil {
		return exists, size, err
	}
	size = stat.Size()
	exists = true
	return exists, size, err
}

func (store *Storage) GetFileReader(collection, filename string) (io.ReadCloser, error) {
	var err error
	var res io.ReadCloser
	filename = store.makeFilepath(collection, filename)
	file, err := os.OpenFile(filename, os.O_RDONLY, 0)
	if err != nil {
		return res, err
	}
	res = file
	return res, err
}

func (store *Storage) GetFileCheksum(collection, filename string) (string, error) {
	var err error
	var res string
	filename = store.makeFilepath(collection, filename)
	file, err := os.OpenFile(filename, os.O_RDONLY, 0)
	if err != nil {
		return res, err
	}
	defer file.Close()
	hasher := NewHasher()
	_, err = io.Copy(hasher.Writer(), file)
	if err != nil {
		return res, err
	}
	res = hasher.Hex()
	return res, err
}

func (store *Storage) WriteTempFile(source io.Reader) (string, int64, string, error) {
	var err error
	var size int64
	var csum string

	tmpName := fmt.Sprintf("%s.tmp", auxuuid.NewUUID())
	tmpPath := store.makeTmppath(tmpName)

	tmpdirpath := store.makeTmpsubdir()
	err = os.MkdirAll(tmpdirpath, 0750)
	if err != nil {
		return tmpName, size, csum, err
	}

	file, err := os.OpenFile(tmpPath, os.O_WRONLY|os.O_CREATE, 0640)
	if err != nil {
		return tmpName, size, csum, err
	}
	defer file.Close()

	hasher := NewHasher()
	mWriter := io.MultiWriter(file, hasher.Writer())
	size, err = io.Copy(mWriter, source)
	if err != nil {
		return tmpName, size, csum, err
	}
	csum = hasher.Hex()
	return tmpName, size, csum, err
}

func (store *Storage) HardlinkFile(tmpName, collection, filename string) error {
	var err error

	dirname := store.makeCollecionpath(collection)
	_, err = os.Stat(dirname)
	if os.IsNotExist(err) {
		err = os.MkdirAll(dirname, 0750)
		if err != nil {
			return err
		}
	}
	if err != nil {
		return err
	}
	filename = store.makeFilepath(collection, filename)
	_, err = os.Stat(dirname)
	if err == nil {
		os.Remove(filename) // TODO: safe removing
	}
	tmpName = store.makeTmppath(tmpName)
	err = os.Link(tmpName, filename)
	if err != nil {
		return err
	}
	err = os.Remove(tmpName)
	if err != nil {
		return err
	}
	return err
}

func (store *Storage) DeleteFile(collection, filename string) error {
	var err error
	filename = store.makeFilepath(collection, filename)
	err = os.Remove(filename)
	if err != nil {
		return err
	}
	// TODO: clean dirs removing
	return err
}

func (store *Storage) ListAllFiles() ([]string, error) {
	names := make([]string, 0)
	var err error
	rootdir := store.makeCollecionpath(string(filepath.Separator))
	walker := func(filename string, fileInfo os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if !fileInfo.Mode().IsRegular() {
			return nil
		}
		name := strings.TrimPrefix(filename, filepath.Clean(rootdir))
		name = filepath.Join(string(filepath.Separator), name)
		names = append(names, name)
		return nil
	}
	err = filepath.Walk(rootdir, walker)
	if err != nil {
		return names, err
	}
	return names, err
}
