package app

import (
	"context"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	goruntime "runtime"
	"strings"
	"sync"
	"time"

	"github.com/wailsapp/wails/v2/pkg/runtime"
	"github.com/xuri/excelize/v2"

	"git.dumerain.org/alban/calcul-astreintes/internal/calc"
	"git.dumerain.org/alban/calcul-astreintes/internal/importer"
	"git.dumerain.org/alban/calcul-astreintes/internal/models"
	"git.dumerain.org/alban/calcul-astreintes/internal/pdf"
	"git.dumerain.org/alban/calcul-astreintes/internal/profiles"
)

var ymRe = regexp.MustCompile(`^(\d{4})-(\d{2})$|^(\d{2})-(\d{4})$`)

func normalizeYearMonth(sheet string) (string, bool) {
	sheet = strings.TrimSpace(sheet)
	m := ymRe.FindStringSubmatch(sheet)
	if m == nil {
		return "", false
	}
	// YYYY-MM
	if m[1] != "" && m[2] != "" {
		return fmt.Sprintf("%s-%s", m[1], m[2]), true
	}
	// MM-YYYY
	if m[3] != "" && m[4] != "" {
		return fmt.Sprintf("%s-%s", m[4], m[3]), true
	}
	return "", false
}

type App struct {
	ctx context.Context

	repo  *profiles.Repo
	rules *models.GlobalRules

	importMu sync.RWMutex
	agents   []models.Agent
	totals   map[string]models.AgentTotals

	lastImportYearMonth string
	lastImportFileBase  string
}

func NewApp() *App {
	repo, rules, err := profiles.NewEmbeddedRepo()
	if err != nil {
		panic(err)
	}

	return &App{
		repo:   repo,
		rules:  rules,
		totals: map[string]models.AgentTotals{},
	}
}

func (a *App) Startup(ctx context.Context) {
	a.ctx = ctx
}

func (a *App) ListProfiles() ([]models.ProfileListItem, error) {
	return a.repo.List()
}

func (a *App) Calculate(req models.CalculateRequest) (models.CalculateResponse, error) {
	profile, err := a.repo.Get(req.ProfileID)
	if err != nil {
		return models.CalculateResponse{}, err
	}
	return calc.Compute(req, profile, *a.rules), nil
}

// ImportMonthlyXLSX : import Excel + stockage en mémoire
// ✅ Mois = nom de feuille (YYYY-MM ou MM-YYYY) normalisé en YYYY-MM
func (a *App) ImportMonthlyXLSX() ([]models.Agent, error) {
	if a.ctx == nil {
		return nil, fmt.Errorf("contexte Wails non initialisé")
	}

	path, err := runtime.OpenFileDialog(a.ctx, runtime.OpenDialogOptions{
		Title: "Importer le fichier Excel des astreintes",
		Filters: []runtime.FileFilter{
			{DisplayName: "Excel (.xlsx)", Pattern: "*.xlsx"},
		},
	})
	if err != nil || path == "" {
		return nil, fmt.Errorf("import annulé")
	}

	// Détection feuille
	f, err := excelize.OpenFile(path)
	if err != nil {
		return nil, err
	}
	defer func() { _ = f.Close() }()

	sheet, err := importer.FindMonthSheet(f)
	if err != nil {
		return nil, err
	}

	yearMonth, ok := normalizeYearMonth(sheet)
	if !ok {
		return nil, fmt.Errorf("nom de feuille invalide pour le mois: %s (attendu YYYY-MM ou MM-YYYY)", sheet)
	}

	agents, totals, err := importer.ParseAgentsAndTotals(path)
	if err != nil {
		return nil, err
	}

	a.importMu.Lock()
	a.agents = agents
	a.totals = totals
	a.lastImportYearMonth = yearMonth // ✅ toujours en YYYY-MM
	a.lastImportFileBase = filepath.Base(path)
	a.importMu.Unlock()

	return agents, nil
}

func (a *App) GetImportedTotals(matricule string) (models.AgentTotals, error) {
	a.importMu.RLock()
	defer a.importMu.RUnlock()

	t, ok := a.totals[matricule]
	if !ok {
		return models.AgentTotals{}, fmt.Errorf("matricule introuvable: %s", matricule)
	}
	return t, nil
}

func (a *App) GetLastImportInfo() (map[string]string, error) {
	a.importMu.RLock()
	defer a.importMu.RUnlock()

	return map[string]string{
		"yearMonth": a.lastImportYearMonth, // ✅ normalisé
		"fileBase":  a.lastImportFileBase,
	}, nil
}

func (a *App) ExportPDF(payload models.ExportPDFRequest) (models.ExportPDFResponse, error) {
	profile, err := a.repo.Get(payload.Request.ProfileID)
	if err != nil {
		return models.ExportPDFResponse{}, err
	}

	res := calc.Compute(payload.Request, profile, *a.rules)
	defaultName := defaultPDFNameV051(payload.Meta)

	savePath, err := runtime.SaveFileDialog(a.ctx, runtime.SaveDialogOptions{
		Title:           "Exporter le calcul en PDF",
		DefaultFilename: defaultName,
		Filters: []runtime.FileFilter{
			{DisplayName: "PDF (*.pdf)", Pattern: "*.pdf"},
		},
	})
	if err != nil || savePath == "" {
		return models.ExportPDFResponse{}, fmt.Errorf("export annulé")
	}

	if filepath.Ext(strings.ToLower(savePath)) != ".pdf" {
		savePath += ".pdf"
	}

	data, err := pdf.BuildPDF(payload.Meta, profile.Label, payload.Request, res)
	if err != nil {
		return models.ExportPDFResponse{}, err
	}

	if err := os.WriteFile(savePath, data, 0o644); err != nil {
		return models.ExportPDFResponse{}, err
	}

	_ = openWithDefaultApp(savePath)
	return models.ExportPDFResponse{Path: savePath}, nil
}

func defaultPDFNameV051(meta models.ExportPDFMeta) string {
	ts := time.Now().Format("2006-01-02-15h04")
	return fmt.Sprintf(
		"%s-%s-%s-%s.pdf",
		ts,
		pdf.SanitizeFilename(meta.Nom),
		pdf.SanitizeFilename(meta.Prenom),
		pdf.SanitizeFilename(meta.Matricule),
	)
}

func openWithDefaultApp(path string) error {
	switch goruntime.GOOS {
	case "windows":
		return exec.Command("cmd", "/c", "start", "", path).Start()
	case "darwin":
		return exec.Command("open", path).Start()
	default:
		return exec.Command("xdg-open", path).Start()
	}
}
