package qslcard

import (
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
)

// TemplateDir returns the asset folder of a stored template:
// <baseDir>/templates/<id>. baseDir is <dataDir>/qsl.
func TemplateDir(baseDir string, id int64) string {
	return filepath.Join(baseDir, "templates", fmt.Sprintf("%d", id))
}

// IsDraftPhoto reports whether a photo reference still points at the user's
// original file (absolute path) rather than a name inside the template dir.
func IsDraftPhoto(ref string) bool { return filepath.IsAbs(ref) }

// ImportPhotos copies every absolute-path photo the template references into
// dir and rewrites the references to the copied names (img_<hash8>.<ext>).
// Users move files around — a saved template must never depend on the
// original location. Already-relative references are left untouched.
func ImportPhotos(t *Template, dir string) error {
	if err := os.MkdirAll(dir, 0o755); err != nil {
		return fmt.Errorf("create template dir: %w", err)
	}
	imported := map[string]string{} // original path → copied name
	importOne := func(ref string) (string, error) {
		if !IsDraftPhoto(ref) {
			return ref, nil
		}
		if name, ok := imported[ref]; ok {
			return name, nil
		}
		name, err := copyPhoto(ref, dir)
		if err != nil {
			return "", err
		}
		imported[ref] = name
		return name, nil
	}
	name, err := importOne(t.Hero.Photo)
	if err != nil {
		return fmt.Errorf("import hero photo: %w", err)
	}
	t.Hero.Photo = name
	for i := range t.Elements {
		if t.Elements[i].Type != ElemInsert {
			continue
		}
		name, err := importOne(t.Elements[i].Photo)
		if err != nil {
			return fmt.Errorf("import insert photo: %w", err)
		}
		t.Elements[i].Photo = name
	}
	return nil
}

// copyPhoto copies src into dir under a content-derived name so identical
// photos dedupe and re-saving is idempotent.
func copyPhoto(src, dir string) (string, error) {
	in, err := os.Open(src)
	if err != nil {
		return "", fmt.Errorf("open photo %s: %w", src, err)
	}
	defer in.Close()
	h := sha256.New()
	if _, err := io.Copy(h, in); err != nil {
		return "", fmt.Errorf("hash photo %s: %w", src, err)
	}
	ext := strings.ToLower(filepath.Ext(src))
	if ext != ".jpg" && ext != ".jpeg" && ext != ".png" {
		return "", fmt.Errorf("unsupported photo type %q (jpeg/png only)", ext)
	}
	name := "img_" + hex.EncodeToString(h.Sum(nil))[:8] + ext
	dst := filepath.Join(dir, name)
	if _, err := os.Stat(dst); err == nil {
		return name, nil // identical content already imported
	}
	if _, err := in.Seek(0, io.SeekStart); err != nil {
		return "", fmt.Errorf("rewind photo %s: %w", src, err)
	}
	out, err := os.Create(dst)
	if err != nil {
		return "", fmt.Errorf("create %s: %w", dst, err)
	}
	defer out.Close()
	if _, err := io.Copy(out, in); err != nil {
		return "", fmt.Errorf("copy photo to %s: %w", dst, err)
	}
	if err := out.Close(); err != nil {
		return "", fmt.Errorf("flush %s: %w", dst, err)
	}
	return name, nil
}

// PhotoExistsIn returns a Validate callback that checks photo references
// against a template asset dir (relative names) or the filesystem (drafts).
func PhotoExistsIn(dir string) func(string) bool {
	return func(ref string) bool {
		p := ref
		if !IsDraftPhoto(ref) {
			p = filepath.Join(dir, ref)
		}
		_, err := os.Stat(p)
		return err == nil
	}
}

// RemoveTemplateDir deletes a template's asset folder (photos + preview).
func RemoveTemplateDir(baseDir string, id int64) error {
	dir := TemplateDir(baseDir, id)
	if err := os.RemoveAll(dir); err != nil {
		return fmt.Errorf("remove template dir %s: %w", dir, err)
	}
	return nil
}
