package fs

import (
	"context"
	"errors"
	"fmt"
	"io/fs"

	"github.com/adrg/frontmatter"
	"gopkg.in/yaml.v3"

	"source.toby3d.me/toby3d/home/internal/domain"
	"source.toby3d.me/toby3d/home/internal/entry"
)

type (
	Page struct {
		Title       string         `yaml:"title"`
		Description string         `yaml:"description"`
		Params      map[string]any `yaml:",inline"`
		Content     []byte         `yaml:"-"`
	}

	fileSystemPageRepository struct {
		dir fs.FS
	}
)

var FrontMatterFormats = []*frontmatter.Format{
	frontmatter.NewFormat(`---`, `---`, yaml.Unmarshal),
}

func NewFileSystemPageRepository(dir fs.FS) entry.Repository {
	return &fileSystemPageRepository{
		dir: dir,
	}
}

func (repo *fileSystemPageRepository) Get(ctx context.Context, lang domain.Language, p string) (*domain.Entry, error) {
	ext := ".md"
	if lang != domain.LanguageUnd {
		ext = "." + lang.Lang() + ext
	}

	target := p + ext

	f, err := repo.dir.Open(target)
	if err != nil {
		return nil, fmt.Errorf("cannot open '%s' entry file: %w", target, err)
	}
	defer f.Close()

	data := &Page{
		Params: make(map[string]any),
	}
	if data.Content, err = frontmatter.Parse(f, data, FrontMatterFormats...); err != nil {
		return nil, fmt.Errorf("cannot parse entry content as FrontMatter: %w", err)
	}

	return &domain.Entry{
		File:         domain.NewPath(target),
		Language:     lang,
		Title:        data.Title,
		Content:      data.Content,
		Description:  data.Description,
		Params:       data.Params,
		Resources:    make([]*domain.Resource, 0),
		Translations: make([]*domain.Entry, 0),
	}, nil
}

func (repo *fileSystemPageRepository) Stat(_ context.Context, l domain.Language, p string) (bool, error) {
	ext := ".md"
	if l != domain.LanguageUnd {
		ext = "." + l.Lang() + ext
	}

	_, err := fs.Stat(repo.dir, p+ext)

	return errors.Is(err, fs.ErrExist), nil
}
