package usecase

import (
	"context"
	"fmt"
	"path"
	"strings"

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

type (
	entryUseCase struct {
		entries   entry.Repository
		resources resource.Repository
	}

	stubEntryUseCase struct {
		entry *domain.Entry
		err   error
	}
)

func NewEntryUseCase(entries entry.Repository, resources resource.Repository) entry.UseCase {
	return &entryUseCase{
		entries:   entries,
		resources: resources,
	}
}

func (ucase *entryUseCase) Do(ctx context.Context, lang domain.Language, p string) (*domain.Entry, error) {
	targets := make([]string, 0)
	hasExt := path.Ext(p) != ""
	head, tail := urlutil.ShiftPath(p)

	if tail == "/" {
		if head = strings.TrimSuffix(head, path.Ext(head)); head == "" {
			head = "index"
		}

		targets = append(targets, head)
	}

	if head != "index" {
		tail = strings.TrimSuffix(tail, path.Ext(tail))
		if !strings.HasSuffix(tail, "/index") {
			if hasExt {
				targets = append([]string{path.Join(head, tail, "index")}, targets...)
			} else {
				targets = append(targets, path.Join(head, tail, "index"))
			}
		} else {
			targets = append(targets, path.Join(head, tail))
		}
	}

	for i := len(targets) - 1; 0 <= i; i-- {
		result, err := ucase.entries.Get(ctx, lang, targets[i])
		if err != nil {
			continue
		}

		if result.Resources, _, err = ucase.resources.Fetch(ctx, result.File.Dir()+"*"); err != nil {
			return result, nil
		}

		for _, res := range result.Resources.GetType(domain.ResourceTypePage) {
			if res.File.TranslationBaseName() != result.File.TranslationBaseName() {
				continue
			}

			translation, err := ucase.entries.Get(ctx, res.File.Language, targets[i])
			if err != nil {
				continue
			}

			result.Translations = append(result.Translations, translation)
		}

		return result, nil
	}

	return nil, fmt.Errorf("cannot find page on path '%s': %w", p, entry.ErrNotExist)
}

func NewStubEntryUseCase(entry *domain.Entry, err error) entry.UseCase {
	return &stubEntryUseCase{
		entry: entry,
		err:   err,
	}
}

func (ucase *stubEntryUseCase) Do(_ context.Context, _ domain.Language, _ string) (*domain.Entry, error) {
	return ucase.entry, ucase.err
}
