package migration

import (
	"context"
	"encoding/json"
	"fmt"
	"strings"

	"github.com/ultisuite/ulti-backend/internal/mail/hosted"
)

// CutoverConfig controls DNS expectations during migration cutover.
type CutoverConfig struct {
	ExpectedMXHosts []string
	RequireMX       bool
}

// CutoverResult is returned when a migration project enters cutover.
type CutoverResult struct {
	Project Project              `json:"project"`
	DNS     hosted.DNSCheckReport `json:"dns"`
}

var ErrCutoverMXNotReady = fmt.Errorf("migration cutover blocked: mx records not pointing to ultimail")

func (s *Service) PreflightCutoverDNS(ctx context.Context, projectID string, cfg CutoverConfig) (hosted.DNSCheckReport, error) {
	domainID, err := s.projectDomainID(ctx, projectID)
	if err != nil {
		return hosted.DNSCheckReport{}, err
	}
	if domainID == "" {
		return hosted.DNSCheckReport{
			Warnings: []string{"project has no linked mail domain; mx/txt checks skipped"},
		}, nil
	}
	if s.hosted == nil {
		return hosted.DNSCheckReport{}, fmt.Errorf("hosted mail service not configured")
	}
	_, report, err := s.hosted.CheckDomainDNS(ctx, domainID, cfg.ExpectedMXHosts)
	return report, err
}

func (s *Service) StartCutover(ctx context.Context, projectID string) (CutoverResult, error) {
	return s.startCutover(ctx, projectID, s.cutover)
}

func (s *Service) startCutover(ctx context.Context, projectID string, cfg CutoverConfig) (CutoverResult, error) {
	domainID, err := s.projectDomainID(ctx, projectID)
	if err != nil {
		return CutoverResult{}, err
	}

	var report hosted.DNSCheckReport
	if domainID != "" {
		if s.hosted == nil {
			return CutoverResult{}, fmt.Errorf("hosted mail service not configured")
		}
		var checkErr error
		_, report, checkErr = s.hosted.CheckDomainDNS(ctx, domainID, cfg.ExpectedMXHosts)
		if checkErr != nil {
			return CutoverResult{}, checkErr
		}
		if report.TXTVerified {
			if _, _, err := s.hosted.VerifyDomainTXTRecord(ctx, domainID); err != nil {
				report.Warnings = append(report.Warnings, "auto txt verify: "+err.Error())
			}
		}
		if report.MXVerified && len(cfg.ExpectedMXHosts) > 0 {
			if _, _, err := s.hosted.VerifyDomainMXRecord(ctx, domainID, cfg.ExpectedMXHosts); err != nil {
				report.Warnings = append(report.Warnings, "auto mx verify: "+err.Error())
			} else {
				report.Warnings = append(report.Warnings, "mx verified and domain marked active")
			}
		} else if !report.MXVerified {
			if cfg.RequireMX {
				return CutoverResult{DNS: report}, ErrCutoverMXNotReady
			}
			report.Warnings = append(report.Warnings, "mx not pointing to ultimail yet; cutover flag set anyway")
		}
	} else {
		report.Warnings = append(report.Warnings, "no domain_id on project; configure mail domain before mx cutover")
	}

	rawDNS, _ := json.Marshal(report)
	_, err = s.db.Exec(ctx, `
		UPDATE migration_projects
		SET status = 'cutover', cutover_at = NOW(), delta_mode = true,
		    cutover_dns_json = $2, updated_at = NOW()
		WHERE id = $1::uuid
	`, projectID, rawDNS)
	if err != nil {
		return CutoverResult{}, err
	}
	_, _ = s.db.Exec(ctx, `
		UPDATE migration_jobs
		SET status = 'pending', error = '', updated_at = NOW()
		WHERE project_id = $1::uuid AND status = 'completed'
	`, projectID)

	sc := newProjectScanner()
	err = s.db.QueryRow(ctx, `
		SELECT `+projectSelectSQL("")+`
		FROM migration_projects WHERE id = $1::uuid
	`, projectID).Scan(sc.targets()...)
	if err != nil {
		return CutoverResult{}, err
	}
	return CutoverResult{Project: sc.result(), DNS: report}, nil
}

func (s *Service) projectDomainID(ctx context.Context, projectID string) (string, error) {
	var domainID string
	err := s.db.QueryRow(ctx, `
		SELECT COALESCE(domain_id::text, '') FROM migration_projects WHERE id = $1::uuid
	`, projectID).Scan(&domainID)
	return domainID, err
}

func ParseCutoverMXHosts(raw string, platformMailDomain, stalwartIMAPHost string) []string {
	var out []string
	for _, part := range strings.Split(raw, ",") {
		part = strings.ToLower(strings.TrimSpace(part))
		if part != "" {
			out = append(out, part)
		}
	}
	if len(out) > 0 {
		return out
	}
	platformMailDomain = strings.ToLower(strings.TrimSpace(platformMailDomain))
	if platformMailDomain != "" {
		out = append(out, "mail."+platformMailDomain)
	}
	stalwartIMAPHost = strings.ToLower(strings.TrimSpace(stalwartIMAPHost))
	if stalwartIMAPHost != "" && !strings.Contains(stalwartIMAPHost, ".") {
		return out
	}
	if stalwartIMAPHost != "" {
		out = append(out, stalwartIMAPHost)
	}
	return out
}
