package onboarding

import (
	"context"
	"encoding/json"
	"fmt"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"time"

	"qctextbuilder/internal/domain"
	"qctextbuilder/internal/qcclient"
	"qctextbuilder/internal/store"
)

var imagePlaceholderPattern = regexp.MustCompile(`^\[\s*(image|img|photo|picture)(\s+(image|img|photo|picture))*\s*\]$`)

var imageLikePathHints = []string{
	"image",
	"img",
	"photo",
	"picture",
	"thumbnail",
	"gallery",
	"logo",
	"icon",
	"avatar",
	"background",
	"banner",
}

type Service struct {
	qc            qcclient.Client
	templateStore store.TemplateStore
	manifestStore store.ManifestStore
}

type FieldPatch struct {
	Path           string
	IsEnabled      *bool
	IsRequiredByUs *bool
	DisplayLabel   *string
	DisplayOrder   *int
	WebsiteSection *string
	Notes          *string
}

func New(qc qcclient.Client, templateStore store.TemplateStore, manifestStore store.ManifestStore) *Service {
	return &Service{
		qc:            qc,
		templateStore: templateStore,
		manifestStore: manifestStore,
	}
}

func (s *Service) OnboardTemplate(ctx context.Context, templateID int64) (*domain.TemplateManifest, []domain.TemplateField, error) {
	template, err := s.templateStore.GetTemplateByID(ctx, templateID)
	if err != nil {
		return nil, nil, fmt.Errorf("get template: %w", err)
	}
	if !template.IsAITemplate {
		return nil, nil, fmt.Errorf("invalid template type: only ai template is allowed")
	}

	req := defaultDiscoveryRequest(templateID)
	data, raw, err := s.qc.GenerateContent(ctx, req)
	if err != nil {
		return nil, nil, fmt.Errorf("generate discovery content: %w", err)
	}

	manifestID := strconv.FormatInt(time.Now().UnixNano(), 10)
	now := time.Now().UTC()
	fields := flattenDiscovery(templateID, manifestID, data)
	flattened, _ := json.Marshal(fields)
	reqRaw, _ := json.Marshal(req)

	manifest := domain.TemplateManifest{
		ID:                    manifestID,
		TemplateID:            templateID,
		Version:               1,
		Source:                "generate-content",
		LanguageUsedDiscovery: "EN",
		DiscoveryPayloadJSON:  reqRaw,
		DiscoveryResponseJSON: raw,
		FlattenedManifestJSON: flattened,
		IsActive:              true,
		CreatedAt:             now,
		UpdatedAt:             now,
	}

	if err := s.manifestStore.CreateManifest(ctx, manifest, fields); err != nil {
		return nil, nil, fmt.Errorf("save manifest: %w", err)
	}
	if err := s.templateStore.SetTemplateManifestStatus(ctx, templateID, "reviewed", true); err != nil {
		return nil, nil, fmt.Errorf("update template status: %w", err)
	}
	return &manifest, fields, nil
}

func (s *Service) UpdateTemplateFields(ctx context.Context, templateID int64, manifestID string, patches []FieldPatch) (*domain.TemplateManifest, []domain.TemplateField, error) {
	template, err := s.templateStore.GetTemplateByID(ctx, templateID)
	if err != nil {
		return nil, nil, fmt.Errorf("get template: %w", err)
	}
	if !template.IsAITemplate {
		return nil, nil, fmt.Errorf("invalid template type: only ai template is allowed")
	}

	manifest, err := s.manifestStore.GetActiveManifestByTemplateID(ctx, templateID)
	if err != nil {
		return nil, nil, fmt.Errorf("get active manifest: %w", err)
	}
	if strings.TrimSpace(manifestID) != "" && manifest.ID != manifestID {
		return nil, nil, fmt.Errorf("manifest mismatch: active=%s requested=%s", manifest.ID, manifestID)
	}

	fields, err := s.manifestStore.ListFieldsByManifestID(ctx, manifest.ID)
	if err != nil {
		return nil, nil, fmt.Errorf("list fields: %w", err)
	}

	byPath := make(map[string]int, len(fields))
	for i := range fields {
		byPath[fields[i].Path] = i
	}

	for _, patch := range patches {
		path := strings.TrimSpace(patch.Path)
		if path == "" {
			return nil, nil, fmt.Errorf("field patch path is required")
		}
		idx, ok := byPath[path]
		if !ok {
			return nil, nil, fmt.Errorf("unknown field path: %s", path)
		}

		if patch.IsEnabled != nil {
			if *patch.IsEnabled && fields[idx].FieldKind != "text" {
				return nil, nil, fmt.Errorf("field %s cannot be enabled for kind %s", path, fields[idx].FieldKind)
			}
			fields[idx].IsEnabled = *patch.IsEnabled
		}
		if patch.IsRequiredByUs != nil {
			fields[idx].IsRequiredByUs = *patch.IsRequiredByUs
		}
		if patch.DisplayLabel != nil {
			fields[idx].DisplayLabel = strings.TrimSpace(*patch.DisplayLabel)
		}
		if patch.DisplayOrder != nil {
			fields[idx].DisplayOrder = *patch.DisplayOrder
		}
		if patch.WebsiteSection != nil {
			fields[idx].WebsiteSection = domain.NormalizeWebsiteSection(*patch.WebsiteSection)
		}
		if patch.Notes != nil {
			fields[idx].Notes = strings.TrimSpace(*patch.Notes)
		}
	}

	if err := s.manifestStore.UpdateFields(ctx, manifest.ID, fields); err != nil {
		return nil, nil, fmt.Errorf("update fields: %w", err)
	}

	sort.Slice(fields, func(i, j int) bool {
		if fields[i].DisplayOrder == fields[j].DisplayOrder {
			return fields[i].Path < fields[j].Path
		}
		return fields[i].DisplayOrder < fields[j].DisplayOrder
	})
	return manifest, fields, nil
}

func defaultDiscoveryRequest(templateID int64) qcclient.GenerateContentRequest {
	return qcclient.GenerateContentRequest{
		TemplateID: templateID,
		GlobalData: map[string]any{
			"companyName":  "Discovery Company",
			"businessType": "dentist",
			"siteLanguage": "EN",
			"email":        "discovery@example.com",
			"phone":        "+41 44 000 00 00",
			"address": map[string]any{
				"line1":      "Discovery Street 1",
				"line2":      "",
				"city":       "Zurich",
				"region":     "ZH",
				"postalCode": "8000",
				"country":    "CH",
			},
		},
		Empty:          false,
		ToneOfVoice:    "Professional",
		TargetAudience: "B2B",
	}
}

func flattenDiscovery(templateID int64, manifestID string, data qcclient.GenerateContentData) []domain.TemplateField {
	fields := make([]domain.TemplateField, 0)
	order := 0
	for section, kv := range data {
		for key, value := range kv {
			sample := fmt.Sprint(value)
			path := section + "." + key
			kind := detectFieldKind(path, sample)
			enabled := kind == "text"

			fields = append(fields, domain.TemplateField{
				ID:         fmt.Sprintf("%s-%d", manifestID, order+1),
				TemplateID: templateID,
				ManifestID: manifestID,
				Section:    section,
				WebsiteSection: domain.SuggestWebsiteSection(domain.TemplateField{
					Section:     section,
					KeyName:     key,
					Path:        path,
					FieldKind:   kind,
					SampleValue: sample,
				}),
				KeyName:      key,
				Path:         path,
				FieldKind:    kind,
				SampleValue:  sample,
				IsEnabled:    enabled,
				DisplayLabel: path,
				DisplayOrder: order,
				Notes:        "",
			})
			order++
		}
	}
	return fields
}

func detectFieldKind(path string, sample string) string {
	sampleTrim := strings.TrimSpace(sample)
	if sampleTrim == "" {
		return "unknown"
	}

	if strings.EqualFold(sampleTrim, "#IMAGE#") {
		return "image"
	}
	if isLikelyImagePlaceholder(sampleTrim) {
		return "image"
	}
	if isLikelyImagePath(path) {
		return "image"
	}
	return "text"
}

func isLikelyImagePlaceholder(sample string) bool {
	normalized := strings.ToLower(strings.TrimSpace(sample))
	if imagePlaceholderPattern.MatchString(normalized) {
		return true
	}
	if normalized == "image" || normalized == "img" || normalized == "photo" || normalized == "picture" {
		return true
	}
	return strings.Contains(normalized, " image image ")
}

func isLikelyImagePath(path string) bool {
	normalized := strings.ToLower(strings.TrimSpace(path))
	for _, hint := range imageLikePathHints {
		if strings.Contains(normalized, hint) {
			return true
		}
	}
	return false
}
