package local

import (
	"io"
	"io/fs"
	"os"
	"path/filepath"

	"gitlink.org.cn/cloudream/common/pkgs/logger"
	clitypes "gitlink.org.cn/cloudream/jcs-pub/client/types"
	"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/storage/types"
)

type PublicStore struct {
	root   string
	detail *clitypes.UserSpaceDetail
}

func NewPublicStore(root string, detail *clitypes.UserSpaceDetail) (*PublicStore, error) {
	return &PublicStore{
		root:   root,
		detail: detail,
	}, nil
}

func (s *PublicStore) Write(objPath string, stream io.Reader) error {
	absObjPath := filepath.Join(s.root, objPath)

	err := os.MkdirAll(filepath.Dir(absObjPath), 0755)
	if err != nil {
		return err
	}

	f, err := os.Create(absObjPath)
	if err != nil {
		return err
	}
	defer f.Close()

	_, err = io.Copy(f, stream)
	if err != nil {
		return err
	}

	return nil
}

func (s *PublicStore) Read(objPath string) (io.ReadCloser, error) {
	absObjPath := filepath.Join(s.root, objPath)
	f, err := os.Open(absObjPath)
	if err != nil {
		return nil, err
	}

	return f, nil
}

func (s *PublicStore) Mkdir(path string) error {
	absObjPath := filepath.Join(s.root, path)
	err := os.MkdirAll(absObjPath, 0755)
	if err != nil {
		return err
	}

	return nil
}

func (s *PublicStore) ListAll(path string) ([]types.PublicStoreEntry, error) {
	absObjPath := filepath.Join(s.root, path)

	var es []types.PublicStoreEntry
	err := filepath.WalkDir(absObjPath, func(path string, d fs.DirEntry, err error) error {
		if err != nil {
			return err
		}

		relaPath, err := filepath.Rel(s.root, path)
		if err != nil {
			return err
		}

		if d.IsDir() {
			es = append(es, types.PublicStoreEntry{
				Path:  filepath.ToSlash(relaPath),
				Size:  0,
				IsDir: true,
			})
			return nil
		}
		info, err := d.Info()
		if err != nil {
			return err
		}

		es = append(es, types.PublicStoreEntry{
			Path:  filepath.ToSlash(relaPath),
			Size:  info.Size(),
			IsDir: false,
		})
		return nil
	})
	if os.IsNotExist(err) {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}

	return es, nil
}

func (s *PublicStore) getLogger() logger.Logger {
	return logger.WithField("PublicStore", "Local").WithField("UserSpace", s.detail.UserSpace)
}
