package services

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

	"github.com/anthropics/anthropic-sdk-go"
	"github.com/anthropics/anthropic-sdk-go/option"
	"github.com/google/uuid"
	"github.com/jmoiron/sqlx"

	"mgit.msbls.de/m/KanzlAI-mGMT/internal/models"
)

type AIService struct {
	client anthropic.Client
	db     *sqlx.DB
	youpcDB *sqlx.DB // read-only connection to youpc.org for similar case finder (may be nil)
}

func NewAIService(apiKey string, db *sqlx.DB, youpcDB *sqlx.DB) *AIService {
	client := anthropic.NewClient(option.WithAPIKey(apiKey))
	return &AIService{client: client, db: db, youpcDB: youpcDB}
}

// ExtractedDeadline represents a deadline extracted by AI from a document.
type ExtractedDeadline struct {
	Title         string  `json:"title"`
	DueDate       *string `json:"due_date"`
	DurationValue int     `json:"duration_value"`
	DurationUnit  string  `json:"duration_unit"`
	Timing        string  `json:"timing"`
	TriggerEvent  string  `json:"trigger_event"`
	RuleReference string  `json:"rule_reference"`
	Confidence    float64 `json:"confidence"`
	SourceQuote   string  `json:"source_quote"`
}

type extractDeadlinesToolInput struct {
	Deadlines []ExtractedDeadline `json:"deadlines"`
}

var deadlineExtractionTool = anthropic.ToolParam{
	Name:        "extract_deadlines",
	Description: anthropic.String("Extract all legal deadlines found in the document. Return each deadline with its details."),
	InputSchema: anthropic.ToolInputSchemaParam{
		Properties: map[string]any{
			"deadlines": map[string]any{
				"type":        "array",
				"description": "List of extracted deadlines",
				"items": map[string]any{
					"type": "object",
					"properties": map[string]any{
						"title": map[string]any{
							"type":        "string",
							"description": "Short title describing the deadline (e.g. 'Statement of Defence', 'Reply to Counterclaim')",
						},
						"due_date": map[string]any{
							"type":        []string{"string", "null"},
							"description": "Absolute due date in YYYY-MM-DD format if determinable, null otherwise",
						},
						"duration_value": map[string]any{
							"type":        "integer",
							"description": "Numeric duration value (e.g. 3 for '3 months')",
						},
						"duration_unit": map[string]any{
							"type":        "string",
							"enum":        []string{"days", "weeks", "months"},
							"description": "Unit of the duration period",
						},
						"timing": map[string]any{
							"type":        "string",
							"enum":        []string{"after", "before"},
							"description": "Whether the deadline is before or after the trigger event",
						},
						"trigger_event": map[string]any{
							"type":        "string",
							"description": "The event that triggers this deadline (e.g. 'service of the Statement of Claim')",
						},
						"rule_reference": map[string]any{
							"type":        "string",
							"description": "Legal rule reference (e.g. 'Rule 23 RoP', 'Rule 222 RoP', '§ 276 ZPO')",
						},
						"confidence": map[string]any{
							"type":        "number",
							"minimum":     0,
							"maximum":     1,
							"description": "Confidence score from 0.0 to 1.0",
						},
						"source_quote": map[string]any{
							"type":        "string",
							"description": "The exact quote from the document where this deadline was found",
						},
					},
					"required": []string{"title", "duration_value", "duration_unit", "timing", "trigger_event", "rule_reference", "confidence", "source_quote"},
				},
			},
		},
		Required: []string{"deadlines"},
	},
}

const extractionSystemPrompt = `You are a legal deadline extraction assistant for German and UPC (Unified Patent Court) patent litigation.

Your task is to extract all legal deadlines, time limits, and procedural time periods from the provided document.

For each deadline found, extract:
- A clear title describing the deadline
- The absolute due date if it can be determined from the document
- The duration (value + unit: days/weeks/months)
- Whether it runs before or after a trigger event
- The trigger event that starts the deadline
- The legal rule reference (e.g. Rule 23 RoP, § 276 ZPO)
- Your confidence level (0.0-1.0) in the extraction
- The exact source quote from the document

Be thorough: extract every deadline mentioned, including conditional ones. If a deadline references another deadline (e.g. "within 2 months of the defence"), capture that relationship in the trigger_event field.

If the document contains no deadlines, return an empty list.`

// ExtractDeadlines sends a document (PDF or text) to Claude for deadline extraction.
func (s *AIService) ExtractDeadlines(ctx context.Context, pdfData []byte, text string) ([]ExtractedDeadline, error) {
	var contentBlocks []anthropic.ContentBlockParamUnion

	if len(pdfData) > 0 {
		encoded := base64.StdEncoding.EncodeToString(pdfData)
		contentBlocks = append(contentBlocks, anthropic.ContentBlockParamUnion{
			OfDocument: &anthropic.DocumentBlockParam{
				Source: anthropic.DocumentBlockParamSourceUnion{
					OfBase64: &anthropic.Base64PDFSourceParam{
						Data: encoded,
					},
				},
			},
		})
		contentBlocks = append(contentBlocks, anthropic.NewTextBlock("Extract all legal deadlines from this document."))
	} else if text != "" {
		contentBlocks = append(contentBlocks, anthropic.NewTextBlock("Extract all legal deadlines from the following text:\n\n"+text))
	} else {
		return nil, fmt.Errorf("either pdf_data or text must be provided")
	}

	msg, err := s.client.Messages.New(ctx, anthropic.MessageNewParams{
		Model:     anthropic.ModelClaudeSonnet4_5,
		MaxTokens: 4096,
		System: []anthropic.TextBlockParam{
			{Text: extractionSystemPrompt},
		},
		Messages: []anthropic.MessageParam{
			anthropic.NewUserMessage(contentBlocks...),
		},
		Tools: []anthropic.ToolUnionParam{
			{OfTool: &deadlineExtractionTool},
		},
		ToolChoice: anthropic.ToolChoiceParamOfTool("extract_deadlines"),
	})
	if err != nil {
		return nil, fmt.Errorf("claude API call: %w", err)
	}

	// Find the tool_use block in the response
	for _, block := range msg.Content {
		if block.Type == "tool_use" && block.Name == "extract_deadlines" {
			var input extractDeadlinesToolInput
			if err := json.Unmarshal(block.Input, &input); err != nil {
				return nil, fmt.Errorf("parsing tool output: %w", err)
			}
			return input.Deadlines, nil
		}
	}

	return nil, fmt.Errorf("no tool_use block in response")
}

const summarizeSystemPrompt = `You are a legal case summary assistant for German and UPC patent litigation case management.

Given a case's details, recent events, and deadlines, produce a concise 2-3 sentence summary of what matters right now. Focus on:
- The most urgent upcoming deadline
- Recent significant events
- The current procedural stage

Write in clear, professional language suitable for a lawyer reviewing their case list. Be specific about dates and deadlines.`

// SummarizeCase generates an AI summary for a case and caches it in the database.
func (s *AIService) SummarizeCase(ctx context.Context, tenantID, caseID uuid.UUID) (string, error) {
	// Load case
	var c models.Case
	err := s.db.GetContext(ctx, &c,
		"SELECT * FROM cases WHERE id = $1 AND tenant_id = $2", caseID, tenantID)
	if err != nil {
		return "", fmt.Errorf("loading case: %w", err)
	}

	// Load recent events
	var events []models.CaseEvent
	if err := s.db.SelectContext(ctx, &events,
		"SELECT * FROM case_events WHERE case_id = $1 AND tenant_id = $2 ORDER BY created_at DESC LIMIT 10",
		caseID, tenantID); err != nil {
		return "", fmt.Errorf("loading events: %w", err)
	}

	// Load active deadlines
	var deadlines []models.Deadline
	if err := s.db.SelectContext(ctx, &deadlines,
		"SELECT * FROM deadlines WHERE case_id = $1 AND tenant_id = $2 AND status = 'active' ORDER BY due_date ASC LIMIT 10",
		caseID, tenantID); err != nil {
		return "", fmt.Errorf("loading deadlines: %w", err)
	}

	// Build context text
	caseInfo := fmt.Sprintf("Case: %s — %s\nStatus: %s", c.CaseNumber, c.Title, c.Status)
	if c.Court != nil {
		caseInfo += fmt.Sprintf("\nCourt: %s", *c.Court)
	}
	if c.CourtRef != nil {
		caseInfo += fmt.Sprintf("\nCourt Reference: %s", *c.CourtRef)
	}
	if c.CaseType != nil {
		caseInfo += fmt.Sprintf("\nType: %s", *c.CaseType)
	}

	eventText := "\n\nRecent Events:"
	if len(events) == 0 {
		eventText += "\nNo events recorded."
	}
	for _, e := range events {
		eventText += fmt.Sprintf("\n- [%s] %s", e.CreatedAt.Format("2006-01-02"), e.Title)
		if e.Description != nil {
			eventText += fmt.Sprintf(": %s", *e.Description)
		}
	}

	deadlineText := "\n\nUpcoming Deadlines:"
	if len(deadlines) == 0 {
		deadlineText += "\nNo active deadlines."
	}
	for _, d := range deadlines {
		deadlineText += fmt.Sprintf("\n- %s: due %s (status: %s)", d.Title, d.DueDate, d.Status)
		if d.Description != nil {
			deadlineText += fmt.Sprintf(" — %s", *d.Description)
		}
	}

	prompt := caseInfo + eventText + deadlineText

	msg, err := s.client.Messages.New(ctx, anthropic.MessageNewParams{
		Model:     anthropic.ModelClaudeSonnet4_5,
		MaxTokens: 512,
		System: []anthropic.TextBlockParam{
			{Text: summarizeSystemPrompt},
		},
		Messages: []anthropic.MessageParam{
			anthropic.NewUserMessage(anthropic.NewTextBlock("Summarize the current state of this case:\n\n" + prompt)),
		},
	})
	if err != nil {
		return "", fmt.Errorf("claude API call: %w", err)
	}

	// Extract text from response
	var summary string
	for _, block := range msg.Content {
		if block.Type == "text" {
			summary += block.Text
		}
	}

	if summary == "" {
		return "", fmt.Errorf("empty response from Claude")
	}

	// Cache summary in database
	_, err = s.db.ExecContext(ctx,
		"UPDATE cases SET ai_summary = $1, updated_at = $2 WHERE id = $3 AND tenant_id = $4",
		summary, time.Now(), caseID, tenantID)
	if err != nil {
		return "", fmt.Errorf("caching summary: %w", err)
	}

	return summary, nil
}

// --- Document Drafting ---

// DocumentDraft represents an AI-generated document draft.
type DocumentDraft struct {
	Title    string `json:"title"`
	Content  string `json:"content"`
	Language string `json:"language"`
}

// templateDescriptions maps template type IDs to descriptions for Claude.
var templateDescriptions = map[string]string{
	"klageschrift":      "Klageschrift (Statement of Claim) — formal complaint initiating legal proceedings",
	"klageerwiderung":   "Klageerwiderung (Statement of Defence) — formal response to a statement of claim",
	"abmahnung":         "Abmahnung (Cease and Desist Letter) — formal warning letter demanding cessation of an activity",
	"schriftsatz":       "Schriftsatz (Legal Brief) — formal legal submission to the court",
	"berufung":          "Berufungsschrift (Appeal Brief) — formal appeal against a court decision",
	"antrag":            "Antrag (Motion/Application) — formal application or motion to the court",
	"stellungnahme":     "Stellungnahme (Statement/Position Paper) — formal response or position paper",
	"gutachten":         "Gutachten (Legal Opinion/Expert Report) — detailed legal analysis or opinion",
	"vertrag":           "Vertrag (Contract/Agreement) — legal contract or agreement between parties",
	"vollmacht":         "Vollmacht (Power of Attorney) — formal authorization document",
	"upc_claim":         "UPC Statement of Claim — claim filed at the Unified Patent Court",
	"upc_defence":       "UPC Statement of Defence — defence filed at the Unified Patent Court",
	"upc_counterclaim":  "UPC Counterclaim for Revocation — counterclaim for patent revocation at the UPC",
	"upc_injunction":    "UPC Application for Provisional Measures — application for injunctive relief at the UPC",
}

const draftDocumentSystemPrompt = `You are an expert legal document drafter for German and UPC (Unified Patent Court) patent litigation.

You draft professional legal documents in the requested language, following proper legal formatting conventions.

Guidelines:
- Use proper legal structure with numbered sections and paragraphs
- Include standard legal formalities (headers, salutations, signatures block)
- Reference relevant legal provisions (BGB, ZPO, UPC Rules of Procedure, etc.)
- Use precise legal terminology appropriate for the jurisdiction
- Include placeholders in [BRACKETS] for information that needs to be filled in
- Base the content on the provided case data and instructions
- Output the document as clean text with proper formatting`

// DraftDocument generates an AI-drafted legal document based on case data and a template type.
func (s *AIService) DraftDocument(ctx context.Context, tenantID, caseID uuid.UUID, templateType, instructions, language string) (*DocumentDraft, error) {
	if language == "" {
		language = "de"
	}

	langLabel := "German"
	if language == "en" {
		langLabel = "English"
	} else if language == "fr" {
		langLabel = "French"
	}

	// Load case data
	var c models.Case
	if err := s.db.GetContext(ctx, &c,
		"SELECT * FROM cases WHERE id = $1 AND tenant_id = $2", caseID, tenantID); err != nil {
		return nil, fmt.Errorf("loading case: %w", err)
	}

	// Load parties
	var parties []models.Party
	_ = s.db.SelectContext(ctx, &parties,
		"SELECT * FROM parties WHERE case_id = $1 AND tenant_id = $2", caseID, tenantID)

	// Load recent events
	var events []models.CaseEvent
	_ = s.db.SelectContext(ctx, &events,
		"SELECT * FROM case_events WHERE case_id = $1 AND tenant_id = $2 ORDER BY created_at DESC LIMIT 15",
		caseID, tenantID)

	// Load active deadlines
	var deadlines []models.Deadline
	_ = s.db.SelectContext(ctx, &deadlines,
		"SELECT * FROM deadlines WHERE case_id = $1 AND tenant_id = $2 AND status = 'active' ORDER BY due_date ASC LIMIT 10",
		caseID, tenantID)

	// Load documents metadata for context
	var documents []models.Document
	_ = s.db.SelectContext(ctx, &documents,
		"SELECT id, title, doc_type, created_at FROM documents WHERE case_id = $1 AND tenant_id = $2 ORDER BY created_at DESC LIMIT 10",
		caseID, tenantID)

	// Build context
	var b strings.Builder
	b.WriteString(fmt.Sprintf("Case: %s — %s\nStatus: %s\n", c.CaseNumber, c.Title, c.Status))
	if c.Court != nil {
		b.WriteString(fmt.Sprintf("Court: %s\n", *c.Court))
	}
	if c.CourtRef != nil {
		b.WriteString(fmt.Sprintf("Court Reference: %s\n", *c.CourtRef))
	}
	if c.CaseType != nil {
		b.WriteString(fmt.Sprintf("Type: %s\n", *c.CaseType))
	}

	if len(parties) > 0 {
		b.WriteString("\nParties:\n")
		for _, p := range parties {
			role := "unknown role"
			if p.Role != nil {
				role = *p.Role
			}
			b.WriteString(fmt.Sprintf("- %s (%s)", p.Name, role))
			if p.Representative != nil {
				b.WriteString(fmt.Sprintf(" — represented by %s", *p.Representative))
			}
			b.WriteString("\n")
		}
	}

	if len(events) > 0 {
		b.WriteString("\nRecent Events:\n")
		for _, e := range events {
			b.WriteString(fmt.Sprintf("- [%s] %s", e.CreatedAt.Format("2006-01-02"), e.Title))
			if e.Description != nil {
				b.WriteString(fmt.Sprintf(": %s", *e.Description))
			}
			b.WriteString("\n")
		}
	}

	if len(deadlines) > 0 {
		b.WriteString("\nUpcoming Deadlines:\n")
		for _, d := range deadlines {
			b.WriteString(fmt.Sprintf("- %s: due %s\n", d.Title, d.DueDate))
		}
	}

	templateDesc, ok := templateDescriptions[templateType]
	if !ok {
		templateDesc = templateType
	}

	prompt := fmt.Sprintf(`Draft a %s for this case in %s.

Document type: %s

Case context:
%s
Additional instructions from the lawyer:
%s

Generate the complete document now.`, templateDesc, langLabel, templateDesc, b.String(), instructions)

	msg, err := s.client.Messages.New(ctx, anthropic.MessageNewParams{
		Model:     anthropic.ModelClaudeSonnet4_20250514,
		MaxTokens: 8192,
		System: []anthropic.TextBlockParam{
			{Text: draftDocumentSystemPrompt},
		},
		Messages: []anthropic.MessageParam{
			anthropic.NewUserMessage(anthropic.NewTextBlock(prompt)),
		},
	})
	if err != nil {
		return nil, fmt.Errorf("claude API call: %w", err)
	}

	var content string
	for _, block := range msg.Content {
		if block.Type == "text" {
			content += block.Text
		}
	}
	if content == "" {
		return nil, fmt.Errorf("empty response from Claude")
	}

	title := fmt.Sprintf("%s — %s", templateDesc, c.CaseNumber)
	return &DocumentDraft{
		Title:    title,
		Content:  content,
		Language: language,
	}, nil
}

// --- Case Strategy ---

// StrategyRecommendation represents an AI-generated case strategy analysis.
type StrategyRecommendation struct {
	Summary        string           `json:"summary"`
	NextSteps      []StrategyStep   `json:"next_steps"`
	RiskAssessment []RiskItem       `json:"risk_assessment"`
	Timeline       []TimelineItem   `json:"timeline"`
}

type StrategyStep struct {
	Priority    string `json:"priority"` // high, medium, low
	Action      string `json:"action"`
	Reasoning   string `json:"reasoning"`
	Deadline    string `json:"deadline,omitempty"`
}

type RiskItem struct {
	Level       string `json:"level"` // high, medium, low
	Risk        string `json:"risk"`
	Mitigation  string `json:"mitigation"`
}

type TimelineItem struct {
	Date        string `json:"date"`
	Event       string `json:"event"`
	Importance  string `json:"importance"` // critical, important, routine
}

type strategyToolInput struct {
	Summary        string         `json:"summary"`
	NextSteps      []StrategyStep `json:"next_steps"`
	RiskAssessment []RiskItem     `json:"risk_assessment"`
	Timeline       []TimelineItem `json:"timeline"`
}

var caseStrategyTool = anthropic.ToolParam{
	Name:        "case_strategy",
	Description: anthropic.String("Provide strategic case analysis with next steps, risk assessment, and timeline optimization."),
	InputSchema: anthropic.ToolInputSchemaParam{
		Properties: map[string]any{
			"summary": map[string]any{
				"type":        "string",
				"description": "Executive summary of the case situation and strategic outlook (2-4 sentences)",
			},
			"next_steps": map[string]any{
				"type":        "array",
				"description": "Recommended next actions in priority order",
				"items": map[string]any{
					"type": "object",
					"properties": map[string]any{
						"priority": map[string]any{
							"type": "string",
							"enum": []string{"high", "medium", "low"},
						},
						"action": map[string]any{
							"type":        "string",
							"description": "Specific recommended action",
						},
						"reasoning": map[string]any{
							"type":        "string",
							"description": "Why this action is recommended",
						},
						"deadline": map[string]any{
							"type":        "string",
							"description": "Suggested deadline in YYYY-MM-DD format, if applicable",
						},
					},
					"required": []string{"priority", "action", "reasoning"},
				},
			},
			"risk_assessment": map[string]any{
				"type":        "array",
				"description": "Key risks and mitigation strategies",
				"items": map[string]any{
					"type": "object",
					"properties": map[string]any{
						"level": map[string]any{
							"type": "string",
							"enum": []string{"high", "medium", "low"},
						},
						"risk": map[string]any{
							"type":        "string",
							"description": "Description of the risk",
						},
						"mitigation": map[string]any{
							"type":        "string",
							"description": "Recommended mitigation strategy",
						},
					},
					"required": []string{"level", "risk", "mitigation"},
				},
			},
			"timeline": map[string]any{
				"type":        "array",
				"description": "Optimized timeline of upcoming milestones and events",
				"items": map[string]any{
					"type": "object",
					"properties": map[string]any{
						"date": map[string]any{
							"type":        "string",
							"description": "Date in YYYY-MM-DD format",
						},
						"event": map[string]any{
							"type":        "string",
							"description": "Description of the milestone or event",
						},
						"importance": map[string]any{
							"type": "string",
							"enum": []string{"critical", "important", "routine"},
						},
					},
					"required": []string{"date", "event", "importance"},
				},
			},
		},
		Required: []string{"summary", "next_steps", "risk_assessment", "timeline"},
	},
}

const caseStrategySystemPrompt = `You are a senior litigation strategist specializing in German law and UPC (Unified Patent Court) patent proceedings.

Analyze the case thoroughly and provide:
1. An executive summary of the current strategic position
2. Prioritized next steps with clear reasoning
3. Risk assessment with mitigation strategies
4. An optimized timeline of upcoming milestones

Consider:
- Procedural deadlines and their implications
- Strength of the parties' positions based on available information
- Potential settlement opportunities
- Cost-efficiency of different strategic approaches
- UPC-specific procedural peculiarities if applicable (bifurcation, preliminary injunctions, etc.)

Be practical and actionable. Avoid generic advice — tailor recommendations to the specific case data provided.`

// CaseStrategy analyzes a case and returns strategic recommendations.
func (s *AIService) CaseStrategy(ctx context.Context, tenantID, caseID uuid.UUID) (*StrategyRecommendation, error) {
	// Load case
	var c models.Case
	if err := s.db.GetContext(ctx, &c,
		"SELECT * FROM cases WHERE id = $1 AND tenant_id = $2", caseID, tenantID); err != nil {
		return nil, fmt.Errorf("loading case: %w", err)
	}

	// Load parties
	var parties []models.Party
	_ = s.db.SelectContext(ctx, &parties,
		"SELECT * FROM parties WHERE case_id = $1 AND tenant_id = $2", caseID, tenantID)

	// Load all events
	var events []models.CaseEvent
	_ = s.db.SelectContext(ctx, &events,
		"SELECT * FROM case_events WHERE case_id = $1 AND tenant_id = $2 ORDER BY created_at DESC LIMIT 25",
		caseID, tenantID)

	// Load all deadlines (active + completed for context)
	var deadlines []models.Deadline
	_ = s.db.SelectContext(ctx, &deadlines,
		"SELECT * FROM deadlines WHERE case_id = $1 AND tenant_id = $2 ORDER BY due_date ASC LIMIT 20",
		caseID, tenantID)

	// Load documents metadata
	var documents []models.Document
	_ = s.db.SelectContext(ctx, &documents,
		"SELECT id, title, doc_type, created_at FROM documents WHERE case_id = $1 AND tenant_id = $2 ORDER BY created_at DESC LIMIT 15",
		caseID, tenantID)

	// Build comprehensive context
	var b strings.Builder
	b.WriteString(fmt.Sprintf("Case: %s — %s\nStatus: %s\n", c.CaseNumber, c.Title, c.Status))
	if c.Court != nil {
		b.WriteString(fmt.Sprintf("Court: %s\n", *c.Court))
	}
	if c.CourtRef != nil {
		b.WriteString(fmt.Sprintf("Court Reference: %s\n", *c.CourtRef))
	}
	if c.CaseType != nil {
		b.WriteString(fmt.Sprintf("Type: %s\n", *c.CaseType))
	}

	if len(parties) > 0 {
		b.WriteString("\nParties:\n")
		for _, p := range parties {
			role := "unknown"
			if p.Role != nil {
				role = *p.Role
			}
			b.WriteString(fmt.Sprintf("- %s (%s)", p.Name, role))
			if p.Representative != nil {
				b.WriteString(fmt.Sprintf(" — represented by %s", *p.Representative))
			}
			b.WriteString("\n")
		}
	}

	if len(events) > 0 {
		b.WriteString("\nCase Events (chronological):\n")
		for _, e := range events {
			b.WriteString(fmt.Sprintf("- [%s] %s", e.CreatedAt.Format("2006-01-02"), e.Title))
			if e.Description != nil {
				b.WriteString(fmt.Sprintf(": %s", *e.Description))
			}
			b.WriteString("\n")
		}
	}

	if len(deadlines) > 0 {
		b.WriteString("\nDeadlines:\n")
		for _, d := range deadlines {
			b.WriteString(fmt.Sprintf("- %s: due %s (status: %s)\n", d.Title, d.DueDate, d.Status))
		}
	}

	if len(documents) > 0 {
		b.WriteString("\nDocuments on file:\n")
		for _, d := range documents {
			docType := ""
			if d.DocType != nil {
				docType = fmt.Sprintf(" [%s]", *d.DocType)
			}
			b.WriteString(fmt.Sprintf("- %s%s (%s)\n", d.Title, docType, d.CreatedAt.Format("2006-01-02")))
		}
	}

	msg, err := s.client.Messages.New(ctx, anthropic.MessageNewParams{
		Model:     anthropic.ModelClaudeOpus4_6,
		MaxTokens: 4096,
		System: []anthropic.TextBlockParam{
			{Text: caseStrategySystemPrompt},
		},
		Messages: []anthropic.MessageParam{
			anthropic.NewUserMessage(anthropic.NewTextBlock("Analyze this case and provide strategic recommendations:\n\n" + b.String())),
		},
		Tools: []anthropic.ToolUnionParam{
			{OfTool: &caseStrategyTool},
		},
		ToolChoice: anthropic.ToolChoiceParamOfTool("case_strategy"),
	})
	if err != nil {
		return nil, fmt.Errorf("claude API call: %w", err)
	}

	for _, block := range msg.Content {
		if block.Type == "tool_use" && block.Name == "case_strategy" {
			var input strategyToolInput
			if err := json.Unmarshal(block.Input, &input); err != nil {
				return nil, fmt.Errorf("parsing strategy output: %w", err)
			}
			result := &StrategyRecommendation{
				Summary:        input.Summary,
				NextSteps:      input.NextSteps,
				RiskAssessment: input.RiskAssessment,
				Timeline:       input.Timeline,
			}
			// Cache in database
			strategyJSON, _ := json.Marshal(result)
			_, _ = s.db.ExecContext(ctx,
				"UPDATE cases SET ai_summary = $1, updated_at = $2 WHERE id = $3 AND tenant_id = $4",
				string(strategyJSON), time.Now(), caseID, tenantID)
			return result, nil
		}
	}

	return nil, fmt.Errorf("no tool_use block in response")
}

// --- Similar Case Finder ---

// SimilarCase represents a UPC case found to be similar.
type SimilarCase struct {
	CaseNumber   string  `json:"case_number"`
	Title        string  `json:"title"`
	Court        string  `json:"court"`
	Date         string  `json:"date"`
	Relevance    float64 `json:"relevance"`    // 0.0-1.0
	Explanation  string  `json:"explanation"`   // why this case is similar
	KeyHoldings  string  `json:"key_holdings"`  // relevant holdings
	URL          string  `json:"url,omitempty"` // link to youpc.org
}

// youpcCase represents a case from the youpc.org database.
type youpcCase struct {
	ID            string  `db:"id" json:"id"`
	CaseNumber    *string `db:"case_number" json:"case_number"`
	Title         *string `db:"title" json:"title"`
	Court         *string `db:"court" json:"court"`
	DecisionDate  *string `db:"decision_date" json:"decision_date"`
	CaseType      *string `db:"case_type" json:"case_type"`
	Outcome       *string `db:"outcome" json:"outcome"`
	PatentNumbers *string `db:"patent_numbers" json:"patent_numbers"`
	Summary       *string `db:"summary" json:"summary"`
	Claimant      *string `db:"claimant" json:"claimant"`
	Defendant     *string `db:"defendant" json:"defendant"`
}

type similarCaseToolInput struct {
	Cases []struct {
		CaseID      string  `json:"case_id"`
		Relevance   float64 `json:"relevance"`
		Explanation string  `json:"explanation"`
		KeyHoldings string  `json:"key_holdings"`
	} `json:"cases"`
}

var similarCaseTool = anthropic.ToolParam{
	Name:        "rank_similar_cases",
	Description: anthropic.String("Rank the provided UPC cases by relevance to the query case and explain why each is similar."),
	InputSchema: anthropic.ToolInputSchemaParam{
		Properties: map[string]any{
			"cases": map[string]any{
				"type":        "array",
				"description": "UPC cases ranked by relevance (most relevant first)",
				"items": map[string]any{
					"type": "object",
					"properties": map[string]any{
						"case_id": map[string]any{
							"type":        "string",
							"description": "The ID of the UPC case from the provided list",
						},
						"relevance": map[string]any{
							"type":        "number",
							"minimum":     0,
							"maximum":     1,
							"description": "Relevance score from 0.0 to 1.0",
						},
						"explanation": map[string]any{
							"type":        "string",
							"description": "Why this case is relevant — what legal issues, parties, patents, or procedural aspects are similar",
						},
						"key_holdings": map[string]any{
							"type":        "string",
							"description": "Key holdings or legal principles from this case that are relevant",
						},
					},
					"required": []string{"case_id", "relevance", "explanation", "key_holdings"},
				},
			},
		},
		Required: []string{"cases"},
	},
}

const similarCaseSystemPrompt = `You are a UPC (Unified Patent Court) case law expert.

Given a case description and a list of UPC cases from the database, rank the cases by relevance and explain why each one is similar or relevant.

Consider:
- Similar patents or technology areas
- Same parties or representatives
- Similar legal issues (infringement, validity, injunctions, etc.)
- Similar procedural situations
- Relevant legal principles that could apply

Only include cases that are genuinely relevant (relevance > 0.3). Order by relevance descending.`

// FindSimilarCases searches the youpc.org database for similar UPC cases.
func (s *AIService) FindSimilarCases(ctx context.Context, tenantID, caseID uuid.UUID, description string) ([]SimilarCase, error) {
	if s.youpcDB == nil {
		return nil, fmt.Errorf("youpc.org database not configured")
	}

	// Build query context from the case (if provided) or description
	var queryText string
	if caseID != uuid.Nil {
		var c models.Case
		if err := s.db.GetContext(ctx, &c,
			"SELECT * FROM cases WHERE id = $1 AND tenant_id = $2", caseID, tenantID); err != nil {
			return nil, fmt.Errorf("loading case: %w", err)
		}

		var parties []models.Party
		_ = s.db.SelectContext(ctx, &parties,
			"SELECT * FROM parties WHERE case_id = $1 AND tenant_id = $2", caseID, tenantID)

		var b strings.Builder
		b.WriteString(fmt.Sprintf("Case: %s — %s\n", c.CaseNumber, c.Title))
		if c.CaseType != nil {
			b.WriteString(fmt.Sprintf("Type: %s\n", *c.CaseType))
		}
		if c.Court != nil {
			b.WriteString(fmt.Sprintf("Court: %s\n", *c.Court))
		}
		for _, p := range parties {
			role := ""
			if p.Role != nil {
				role = *p.Role
			}
			b.WriteString(fmt.Sprintf("Party: %s (%s)\n", p.Name, role))
		}
		if description != "" {
			b.WriteString(fmt.Sprintf("\nAdditional context: %s\n", description))
		}
		queryText = b.String()
	} else if description != "" {
		queryText = description
	} else {
		return nil, fmt.Errorf("either case_id or description must be provided")
	}

	// Query youpc.org database for candidate cases
	// Search by text similarity across case titles, summaries, party names
	var candidates []youpcCase
	err := s.youpcDB.SelectContext(ctx, &candidates, `
		SELECT
			id,
			case_number,
			title,
			court,
			decision_date,
			case_type,
			outcome,
			patent_numbers,
			summary,
			claimant,
			defendant
		FROM mlex.cases
		ORDER BY decision_date DESC NULLS LAST
		LIMIT 50
	`)
	if err != nil {
		return nil, fmt.Errorf("querying youpc.org cases: %w", err)
	}

	if len(candidates) == 0 {
		return []SimilarCase{}, nil
	}

	// Build candidate list for Claude
	var candidateText strings.Builder
	for _, c := range candidates {
		candidateText.WriteString(fmt.Sprintf("ID: %s\n", c.ID))
		if c.CaseNumber != nil {
			candidateText.WriteString(fmt.Sprintf("Case Number: %s\n", *c.CaseNumber))
		}
		if c.Title != nil {
			candidateText.WriteString(fmt.Sprintf("Title: %s\n", *c.Title))
		}
		if c.Court != nil {
			candidateText.WriteString(fmt.Sprintf("Court: %s\n", *c.Court))
		}
		if c.DecisionDate != nil {
			candidateText.WriteString(fmt.Sprintf("Decision Date: %s\n", *c.DecisionDate))
		}
		if c.CaseType != nil {
			candidateText.WriteString(fmt.Sprintf("Type: %s\n", *c.CaseType))
		}
		if c.Outcome != nil {
			candidateText.WriteString(fmt.Sprintf("Outcome: %s\n", *c.Outcome))
		}
		if c.PatentNumbers != nil {
			candidateText.WriteString(fmt.Sprintf("Patents: %s\n", *c.PatentNumbers))
		}
		if c.Claimant != nil {
			candidateText.WriteString(fmt.Sprintf("Claimant: %s\n", *c.Claimant))
		}
		if c.Defendant != nil {
			candidateText.WriteString(fmt.Sprintf("Defendant: %s\n", *c.Defendant))
		}
		if c.Summary != nil {
			candidateText.WriteString(fmt.Sprintf("Summary: %s\n", *c.Summary))
		}
		candidateText.WriteString("---\n")
	}

	prompt := fmt.Sprintf(`Find UPC cases relevant to this matter:

%s

Here are the UPC cases from the database to evaluate:

%s

Rank only the genuinely relevant cases by similarity.`, queryText, candidateText.String())

	msg, err := s.client.Messages.New(ctx, anthropic.MessageNewParams{
		Model:     anthropic.ModelClaudeSonnet4_20250514,
		MaxTokens: 4096,
		System: []anthropic.TextBlockParam{
			{Text: similarCaseSystemPrompt},
		},
		Messages: []anthropic.MessageParam{
			anthropic.NewUserMessage(anthropic.NewTextBlock(prompt)),
		},
		Tools: []anthropic.ToolUnionParam{
			{OfTool: &similarCaseTool},
		},
		ToolChoice: anthropic.ToolChoiceParamOfTool("rank_similar_cases"),
	})
	if err != nil {
		return nil, fmt.Errorf("claude API call: %w", err)
	}

	for _, block := range msg.Content {
		if block.Type == "tool_use" && block.Name == "rank_similar_cases" {
			var input similarCaseToolInput
			if err := json.Unmarshal(block.Input, &input); err != nil {
				return nil, fmt.Errorf("parsing similar cases output: %w", err)
			}

			// Build lookup map for candidate data
			candidateMap := make(map[string]youpcCase)
			for _, c := range candidates {
				candidateMap[c.ID] = c
			}

			var results []SimilarCase
			for _, ranked := range input.Cases {
				if ranked.Relevance < 0.3 {
					continue
				}
				c, ok := candidateMap[ranked.CaseID]
				if !ok {
					continue
				}
				sc := SimilarCase{
					Relevance:   ranked.Relevance,
					Explanation: ranked.Explanation,
					KeyHoldings: ranked.KeyHoldings,
				}
				if c.CaseNumber != nil {
					sc.CaseNumber = *c.CaseNumber
				}
				if c.Title != nil {
					sc.Title = *c.Title
				}
				if c.Court != nil {
					sc.Court = *c.Court
				}
				if c.DecisionDate != nil {
					sc.Date = *c.DecisionDate
				}
				if c.CaseNumber != nil {
					sc.URL = fmt.Sprintf("https://youpc.org/cases/%s", *c.CaseNumber)
				}
				results = append(results, sc)
			}

			return results, nil
		}
	}

	return nil, fmt.Errorf("no tool_use block in response")
}
