"""Django models for observability tables.

These models map to Prisma-managed tables for LLM tracking, step tracing,
and error monitoring in the optimization pipeline.
"""

from __future__ import annotations

import uuid
from typing import ClassVar

from django.db import models


class LLMCall(models.Model):
    objects: ClassVar[models.Manager[LLMCall]]
    """Track LLM API calls with full prompt/response data for prompt engineering analysis."""

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    trace_id = models.CharField(max_length=36)
    call_type = models.CharField(max_length=50)  # 'optimization', 'test_generation', 'ranking', 'refinement'
    model_name = models.CharField(max_length=100)  # 'gpt-4', 'gpt-3.5-turbo', etc.

    # Prompt capture (full content for prompt engineering analysis)
    system_prompt = models.TextField()
    user_prompt = models.TextField()
    messages = models.JSONField()  # Full messages array sent to LLM

    # Request configuration
    temperature = models.FloatField(null=True, blank=True)
    n_candidates = models.IntegerField(null=True, blank=True)  # Number of completions requested
    max_tokens = models.IntegerField(null=True, blank=True)

    # Response capture
    raw_response = models.TextField(null=True, blank=True)  # Full LLM response before parsing
    parsed_response = models.JSONField(null=True, blank=True)  # Structured parsed response

    # Token usage and cost
    prompt_tokens = models.IntegerField(null=True, blank=True)
    completion_tokens = models.IntegerField(null=True, blank=True)
    total_tokens = models.IntegerField(null=True, blank=True)
    llm_cost = models.FloatField(null=True, blank=True)

    # Timing
    latency_ms = models.IntegerField(null=True, blank=True)

    # Status and errors
    status = models.CharField(max_length=20)  # 'success', 'failed', 'partial_success'
    retry_count = models.IntegerField(default=0)
    error_type = models.CharField(max_length=50, null=True, blank=True)
    error_message = models.TextField(null=True, blank=True)

    # Parsing results
    parsing_status = models.CharField(max_length=20, null=True, blank=True)  # 'success', 'failed', 'partial'
    candidates_generated = models.IntegerField(null=True, blank=True, default=0)
    candidates_valid = models.IntegerField(null=True, blank=True, default=0)  # Passed syntax validation
    parsing_errors = models.JSONField(null=True, blank=True)  # Details of parsing failures

    # Context
    user_id = models.CharField(max_length=255, null=True, blank=True)
    python_version = models.CharField(max_length=20, null=True, blank=True)
    is_async = models.BooleanField(default=False)
    context = models.JSONField(null=True, blank=True)  # Additional metadata

    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        managed = False
        db_table = "llm_calls"
        indexes = [
            models.Index(fields=["trace_id"]),
            models.Index(fields=["call_type"]),
            models.Index(fields=["model_name"]),
            models.Index(fields=["status"]),
            models.Index(fields=["-created_at"]),
            models.Index(fields=["call_type", "status"]),
            models.Index(fields=["parsing_status"]),
        ]

    def __str__(self):
        return f"LLMCall({self.call_type}, {self.model_name}, {self.status})"


class OptimizationError(models.Model):
    objects: ClassVar[models.Manager[OptimizationError]]
    """Comprehensive error tracking with classification and recovery attempts."""

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    trace_id = models.CharField(max_length=36)
    llm_call_id = models.UUIDField(null=True, blank=True)

    # Error classification
    error_type = models.CharField(
        max_length=50
    )  # 'validation', 'llm_api', 'llm_parsing', 'test_failure', 'compilation'
    error_category = models.CharField(max_length=50)  # 'user_error', 'system_error', 'llm_error', 'infrastructure'
    severity = models.CharField(max_length=20)  # 'critical', 'error', 'warning', 'info'

    # Error details
    error_message = models.TextField()
    error_code = models.CharField(max_length=50, null=True, blank=True)
    stack_trace = models.TextField(null=True, blank=True)
    context = models.JSONField(null=True, blank=True)  # Additional error context including test failure details

    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        managed = False
        db_table = "optimization_errors"
        indexes = [
            models.Index(fields=["trace_id"]),
            models.Index(fields=["error_type"]),
            models.Index(fields=["error_category"]),
            models.Index(fields=["severity"]),
            models.Index(fields=["llm_call_id"]),
        ]

    def __str__(self):
        return f"OptimizationError({self.error_type}, {self.severity})"
