package training

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

	"github.com/breakpilot/ai-compliance-sdk/internal/llm"
	"github.com/google/uuid"
)

// ContentGenerator generates training content and quiz questions via LLM
type ContentGenerator struct {
	registry    *llm.ProviderRegistry
	piiDetector *llm.PIIDetector
	store       *Store
	ttsClient   *TTSClient
}

// NewContentGenerator creates a new content generator
func NewContentGenerator(registry *llm.ProviderRegistry, piiDetector *llm.PIIDetector, store *Store, ttsClient *TTSClient) *ContentGenerator {
	return &ContentGenerator{
		registry:    registry,
		piiDetector: piiDetector,
		store:       store,
		ttsClient:   ttsClient,
	}
}

// GenerateModuleContent generates training content for a module via LLM
func (g *ContentGenerator) GenerateModuleContent(ctx context.Context, module TrainingModule, language string) (*ModuleContent, error) {
	if language == "" {
		language = "de"
	}

	prompt := buildContentPrompt(module, language)

	resp, err := g.registry.Chat(ctx, &llm.ChatRequest{
		Messages: []llm.Message{
			{Role: "system", Content: getContentSystemPrompt(language)},
			{Role: "user", Content: prompt},
		},
		Temperature: 0.15,
		MaxTokens:   4096,
	})
	if err != nil {
		return nil, fmt.Errorf("LLM content generation failed: %w", err)
	}

	contentBody := resp.Message.Content

	// PII check on generated content
	if g.piiDetector != nil && g.piiDetector.ContainsPII(contentBody) {
		findings := g.piiDetector.FindPII(contentBody)
		for _, f := range findings {
			contentBody = strings.ReplaceAll(contentBody, f.Match, "[REDACTED]")
		}
	}

	// Create summary (first 200 chars)
	summary := contentBody
	if len(summary) > 200 {
		summary = summary[:200] + "..."
	}

	content := &ModuleContent{
		ModuleID:      module.ID,
		ContentFormat: ContentFormatMarkdown,
		ContentBody:   contentBody,
		Summary:       summary,
		GeneratedBy:   "llm_" + resp.Provider,
		LLMModel:      resp.Model,
		IsPublished:   false,
	}

	if err := g.store.CreateModuleContent(ctx, content); err != nil {
		return nil, fmt.Errorf("failed to save content: %w", err)
	}

	// Audit log
	g.store.LogAction(ctx, &AuditLogEntry{
		TenantID:   module.TenantID,
		Action:     AuditActionContentGenerated,
		EntityType: AuditEntityModule,
		EntityID:   &module.ID,
		Details: map[string]interface{}{
			"module_code": module.ModuleCode,
			"provider":    resp.Provider,
			"model":       resp.Model,
			"content_id":  content.ID.String(),
			"version":     content.Version,
			"tokens_used": resp.Usage.TotalTokens,
		},
	})

	return content, nil
}

// GenerateQuizQuestions generates quiz questions for a module based on its content
func (g *ContentGenerator) GenerateQuizQuestions(ctx context.Context, module TrainingModule, count int) ([]QuizQuestion, error) {
	if count <= 0 {
		count = 5
	}

	// Get the published content for context
	content, err := g.store.GetPublishedContent(ctx, module.ID)
	if err != nil {
		return nil, err
	}

	contentContext := ""
	if content != nil {
		contentContext = content.ContentBody
	}

	prompt := buildQuizPrompt(module, contentContext, count)

	resp, err := g.registry.Chat(ctx, &llm.ChatRequest{
		Messages: []llm.Message{
			{Role: "system", Content: getQuizSystemPrompt()},
			{Role: "user", Content: prompt},
		},
		Temperature: 0.2,
		MaxTokens:   4096,
	})
	if err != nil {
		return nil, fmt.Errorf("LLM quiz generation failed: %w", err)
	}

	// Parse the JSON response
	questions, err := parseQuizResponse(resp.Message.Content, module.ID)
	if err != nil {
		return nil, fmt.Errorf("failed to parse quiz response: %w", err)
	}

	// Save questions to store
	for i := range questions {
		questions[i].SortOrder = i + 1
		if err := g.store.CreateQuizQuestion(ctx, &questions[i]); err != nil {
			return nil, fmt.Errorf("failed to save question %d: %w", i+1, err)
		}
	}

	return questions, nil
}

// GenerateAllModuleContent generates text content for all modules that don't have published content yet
func (g *ContentGenerator) GenerateAllModuleContent(ctx context.Context, tenantID uuid.UUID, language string) (*BulkResult, error) {
	if language == "" {
		language = "de"
	}

	modules, _, err := g.store.ListModules(ctx, tenantID, &ModuleFilters{Limit: 100})
	if err != nil {
		return nil, fmt.Errorf("failed to list modules: %w", err)
	}

	result := &BulkResult{}
	for _, module := range modules {
		// Check if module already has published content
		content, _ := g.store.GetPublishedContent(ctx, module.ID)
		if content != nil {
			result.Skipped++
			continue
		}

		_, err := g.GenerateModuleContent(ctx, module, language)
		if err != nil {
			result.Errors = append(result.Errors, fmt.Sprintf("%s: %v", module.ModuleCode, err))
			continue
		}
		result.Generated++
	}

	return result, nil
}

// GenerateAllQuizQuestions generates quiz questions for all modules that don't have questions yet
func (g *ContentGenerator) GenerateAllQuizQuestions(ctx context.Context, tenantID uuid.UUID, count int) (*BulkResult, error) {
	if count <= 0 {
		count = 5
	}

	modules, _, err := g.store.ListModules(ctx, tenantID, &ModuleFilters{Limit: 100})
	if err != nil {
		return nil, fmt.Errorf("failed to list modules: %w", err)
	}

	result := &BulkResult{}
	for _, module := range modules {
		// Check if module already has quiz questions
		questions, _ := g.store.ListQuizQuestions(ctx, module.ID)
		if len(questions) > 0 {
			result.Skipped++
			continue
		}

		_, err := g.GenerateQuizQuestions(ctx, module, count)
		if err != nil {
			result.Errors = append(result.Errors, fmt.Sprintf("%s: %v", module.ModuleCode, err))
			continue
		}
		result.Generated++
	}

	return result, nil
}

// GenerateBlockContent generates training content for a module based on linked canonical controls
func (g *ContentGenerator) GenerateBlockContent(
	ctx context.Context,
	module TrainingModule,
	controls []CanonicalControlSummary,
	language string,
) (*ModuleContent, error) {
	if language == "" {
		language = "de"
	}

	prompt := buildBlockContentPrompt(module, controls, language)

	resp, err := g.registry.Chat(ctx, &llm.ChatRequest{
		Messages: []llm.Message{
			{Role: "system", Content: getContentSystemPrompt(language)},
			{Role: "user", Content: prompt},
		},
		Temperature: 0.15,
		MaxTokens:   8192,
	})
	if err != nil {
		return nil, fmt.Errorf("LLM block content generation failed: %w", err)
	}

	contentBody := resp.Message.Content

	// PII check
	if g.piiDetector != nil && g.piiDetector.ContainsPII(contentBody) {
		findings := g.piiDetector.FindPII(contentBody)
		for _, f := range findings {
			contentBody = strings.ReplaceAll(contentBody, f.Match, "[REDACTED]")
		}
	}

	summary := contentBody
	if len(summary) > 200 {
		summary = summary[:200] + "..."
	}

	content := &ModuleContent{
		ModuleID:      module.ID,
		ContentFormat: ContentFormatMarkdown,
		ContentBody:   contentBody,
		Summary:       summary,
		GeneratedBy:   "llm_block_" + resp.Provider,
		LLMModel:      resp.Model,
		IsPublished:   false,
	}

	if err := g.store.CreateModuleContent(ctx, content); err != nil {
		return nil, fmt.Errorf("failed to save block content: %w", err)
	}

	// Audit log
	g.store.LogAction(ctx, &AuditLogEntry{
		TenantID:   module.TenantID,
		Action:     AuditActionContentGenerated,
		EntityType: AuditEntityModule,
		EntityID:   &module.ID,
		Details: map[string]interface{}{
			"module_code":    module.ModuleCode,
			"provider":       resp.Provider,
			"model":          resp.Model,
			"content_id":     content.ID.String(),
			"version":        content.Version,
			"tokens_used":    resp.Usage.TotalTokens,
			"controls_count": len(controls),
			"source":         "block_generator",
		},
	})

	return content, nil
}

// ============================================================================
// Prompt Templates
// ============================================================================

func getContentSystemPrompt(language string) string {
	if language == "en" {
		return "You are a compliance training content expert. Generate professional, accurate training material in Markdown format. Focus on practical relevance and legal accuracy. Do not include any personal data or fictional names."
	}
	return "Du bist ein Experte fuer Compliance-Schulungsinhalte. Erstelle professionelle, praezise Schulungsmaterialien im Markdown-Format. Fokussiere dich auf praktische Relevanz und rechtliche Genauigkeit. Verwende keine personenbezogenen Daten oder fiktiven Namen."
}

func getQuizSystemPrompt() string {
	return `Du bist ein Experte fuer Compliance-Pruefungsfragen. Erstelle Multiple-Choice-Fragen als JSON-Array.
Jede Frage hat genau 4 Antwortoptionen, davon genau eine richtige.
Antworte NUR mit dem JSON-Array, ohne zusaetzlichen Text.

Format:
[
  {
    "question": "Frage hier?",
    "options": ["Option A", "Option B", "Option C", "Option D"],
    "correct_index": 0,
    "explanation": "Erklaerung warum Option A richtig ist.",
    "difficulty": "medium"
  }
]`
}

func buildContentPrompt(module TrainingModule, language string) string {
	regulationLabels := map[RegulationArea]string{
		RegulationDSGVO:     "Datenschutz-Grundverordnung (DSGVO)",
		RegulationNIS2:      "NIS-2-Richtlinie",
		RegulationISO27001:  "ISO 27001 / ISMS",
		RegulationAIAct:     "EU AI Act / KI-Verordnung",
		RegulationGeschGehG: "Geschaeftsgeheimnisgesetz (GeschGehG)",
		RegulationHinSchG:   "Hinweisgeberschutzgesetz (HinSchG)",
	}

	regulation := regulationLabels[module.RegulationArea]
	if regulation == "" {
		regulation = string(module.RegulationArea)
	}

	return fmt.Sprintf(`Erstelle Schulungsmaterial fuer folgendes Compliance-Modul:

**Modulcode:** %s
**Titel:** %s
**Beschreibung:** %s
**Regulierungsbereich:** %s
**Dauer:** %d Minuten
**NIS2-relevant:** %v

Das Material soll:
1. Eine kurze Einfuehrung in das Thema geben
2. Die wichtigsten rechtlichen Grundlagen erklaeren
3. Praktische Handlungsanweisungen fuer den Arbeitsalltag enthalten
4. Typische Fehler und Risiken aufzeigen
5. Eine Zusammenfassung der Kernpunkte bieten

Verwende klare, verstaendliche Sprache. Zielgruppe sind Mitarbeiter in Unternehmen (50-1.500 MA).
Formatiere den Inhalt als Markdown mit Ueberschriften, Aufzaehlungen und Hervorhebungen.`,
		module.ModuleCode, module.Title, module.Description,
		regulation, module.DurationMinutes, module.NIS2Relevant)
}

func buildQuizPrompt(module TrainingModule, contentContext string, count int) string {
	prompt := fmt.Sprintf(`Erstelle %d Multiple-Choice-Pruefungsfragen fuer das Compliance-Modul:

**Modulcode:** %s
**Titel:** %s
**Regulierungsbereich:** %s`, count, module.ModuleCode, module.Title, string(module.RegulationArea))

	if contentContext != "" {
		// Truncate content to avoid token limit
		if len(contentContext) > 3000 {
			contentContext = contentContext[:3000] + "..."
		}
		prompt += fmt.Sprintf(`

**Schulungsinhalt als Kontext:**
%s`, contentContext)
	}

	prompt += fmt.Sprintf(`

Erstelle genau %d Fragen mit je 4 Antwortoptionen.
Verteile die Schwierigkeitsgrade: easy, medium, hard.
Antworte NUR mit dem JSON-Array.`, count)

	return prompt
}

// buildBlockContentPrompt creates a prompt that incorporates canonical controls
func buildBlockContentPrompt(module TrainingModule, controls []CanonicalControlSummary, language string) string {
	var sb strings.Builder

	if language == "en" {
		sb.WriteString(fmt.Sprintf("Create training material for the following compliance module:\n\n"))
		sb.WriteString(fmt.Sprintf("**Module Code:** %s\n", module.ModuleCode))
		sb.WriteString(fmt.Sprintf("**Title:** %s\n", module.Title))
		sb.WriteString(fmt.Sprintf("**Duration:** %d minutes\n\n", module.DurationMinutes))
		sb.WriteString(fmt.Sprintf("This module is based on %d security controls:\n\n", len(controls)))
	} else {
		sb.WriteString(fmt.Sprintf("Erstelle Schulungsmaterial fuer folgendes Compliance-Modul:\n\n"))
		sb.WriteString(fmt.Sprintf("**Modulcode:** %s\n", module.ModuleCode))
		sb.WriteString(fmt.Sprintf("**Titel:** %s\n", module.Title))
		sb.WriteString(fmt.Sprintf("**Dauer:** %d Minuten\n\n", module.DurationMinutes))
		sb.WriteString(fmt.Sprintf("Dieses Modul basiert auf %d Sicherheits-Controls:\n\n", len(controls)))
	}

	for i, ctrl := range controls {
		sb.WriteString(fmt.Sprintf("### Control %d: %s — %s\n", i+1, ctrl.ControlID, ctrl.Title))
		sb.WriteString(fmt.Sprintf("**Ziel:** %s\n", ctrl.Objective))
		if len(ctrl.Requirements) > 0 {
			sb.WriteString("**Anforderungen:**\n")
			for _, req := range ctrl.Requirements {
				sb.WriteString(fmt.Sprintf("- %s\n", req))
			}
		}
		sb.WriteString("\n")
	}

	if language == "en" {
		sb.WriteString(`Create the material as Markdown:
1. Introduction: Why are these controls important?
2. Per control: Explanation, practical tips, examples
3. Summary + action items
4. Checklist for daily work

Use clear, understandable language. Target audience: employees in companies (50-1,500 employees).`)
	} else {
		sb.WriteString(`Erstelle das Material als Markdown:
1. Einfuehrung: Warum sind diese Controls wichtig?
2. Pro Control: Erklaerung, praktische Hinweise, Beispiele
3. Zusammenfassung + Handlungsanweisungen
4. Checkliste fuer den Alltag

Verwende klare, verstaendliche Sprache. Zielgruppe sind Mitarbeiter in Unternehmen (50-1.500 MA).
Formatiere den Inhalt als Markdown mit Ueberschriften, Aufzaehlungen und Hervorhebungen.`)
	}

	return sb.String()
}

// parseQuizResponse parses LLM JSON response into QuizQuestion structs
func parseQuizResponse(response string, moduleID uuid.UUID) ([]QuizQuestion, error) {
	// Try to extract JSON from the response (LLM might add text around it)
	jsonStr := response
	start := strings.Index(response, "[")
	end := strings.LastIndex(response, "]")
	if start >= 0 && end > start {
		jsonStr = response[start : end+1]
	}

	type rawQuestion struct {
		Question     string   `json:"question"`
		Options      []string `json:"options"`
		CorrectIndex int      `json:"correct_index"`
		Explanation  string   `json:"explanation"`
		Difficulty   string   `json:"difficulty"`
	}

	var rawQuestions []rawQuestion
	if err := json.Unmarshal([]byte(jsonStr), &rawQuestions); err != nil {
		return nil, fmt.Errorf("invalid JSON from LLM: %w", err)
	}

	var questions []QuizQuestion
	for _, rq := range rawQuestions {
		difficulty := Difficulty(rq.Difficulty)
		if difficulty != DifficultyEasy && difficulty != DifficultyMedium && difficulty != DifficultyHard {
			difficulty = DifficultyMedium
		}

		q := QuizQuestion{
			ModuleID:     moduleID,
			Question:     rq.Question,
			Options:      rq.Options,
			CorrectIndex: rq.CorrectIndex,
			Explanation:  rq.Explanation,
			Difficulty:   difficulty,
			IsActive:     true,
		}

		if len(q.Options) != 4 {
			continue // Skip malformed questions
		}
		if q.CorrectIndex < 0 || q.CorrectIndex >= len(q.Options) {
			continue
		}

		questions = append(questions, q)
	}

	if questions == nil {
		questions = []QuizQuestion{}
	}

	return questions, nil
}

func truncateText(text string, maxLen int) string {
	if len(text) <= maxLen {
		return text
	}
	return text[:maxLen] + "..."
}
