"""
Pydantic schemas generator.

Generates Base, Create, Read, Update schemas for FastAPI endpoints.
"""

from ..base import BaseGenerator
from ...ir.models import GeneratedFile, ParsedModel, ParsedField, RelationType
from ...parser.type_mapper import TypeMapper


class SchemasGenerator(BaseGenerator):
    """
    Generates Pydantic schemas for FastAPI.

    Creates Base, Create, Read, Update schemas per model.
    """

    def __init__(self, context):
        super().__init__(context)
        self.type_mapper = TypeMapper()

    def generate(self) -> list[GeneratedFile]:
        """Generate schemas files for all apps."""
        files: list[GeneratedFile] = []

        for app_label, models in self.context.group_models_by_app().items():
            content = self._generate_schemas_file(app_label, models)
            files.append(self.create_file(f"{app_label}/schemas.py", content))

        return files

    def _generate_schemas_file(self, app_label: str, models: list[ParsedModel]) -> str:
        """Generate schemas.py content for an app."""
        self.type_mapper.reset_imports()

        # Process all models
        schema_definitions = []
        for model in models:
            schemas = self._generate_model_schemas(model)
            schema_definitions.append(schemas)

        # Build imports
        imports = self._build_imports()

        parts = [
            '"""',
            f"Pydantic schemas for {app_label}.",
            "",
            "Auto-generated by django-cfg FastAPI ORM Generator.",
            '"""',
            "",
            imports,
            "",
        ]

        parts.extend(schema_definitions)

        return "\n".join(parts)

    def _generate_model_schemas(self, model: ParsedModel) -> str:
        """Generate all schemas for a model (Base, Create, Read, Update)."""
        lines = []
        suffix = self.config.schema_suffix

        # Get fields for schemas
        base_fields = self._get_base_fields(model)
        create_fields = self._get_create_fields(model)
        pk_field = model.pk_field

        # Base schema (for Read - includes all fields)
        lines.append(f"class {model.name}Base(BaseModel):")
        if self.config.add_docstrings:
            lines.append(f'    """Base schema for {model.name}."""')
        lines.append("    model_config = ConfigDict(from_attributes=True)")
        lines.append("")
        for field_def in base_fields:
            lines.append(f"    {field_def}")
        if not base_fields:
            lines.append("    pass")
        lines.append("")
        lines.append("")

        # Create schema (excludes auto_now, auto_now_add fields)
        lines.append(f"class {model.name}Create(BaseModel):")
        if self.config.add_docstrings:
            lines.append(f'    """Schema for creating {model.name}."""')
        lines.append("    model_config = ConfigDict(from_attributes=True)")
        lines.append("")
        for field_def in create_fields:
            lines.append(f"    {field_def}")
        if not create_fields:
            lines.append("    pass")
        lines.append("")
        lines.append("")

        # Read schema (includes ID)
        lines.append(f"class {model.name}Read({model.name}Base):")
        if self.config.add_docstrings:
            lines.append(f'    """Schema for reading {model.name}."""')
        if pk_field:
            lines.append(f"    {pk_field.name}: int")
        else:
            lines.append("    id: int")
        lines.append("")
        lines.append("")

        # Update schema (all fields optional)
        lines.append(f"class {model.name}Update(BaseModel):")
        if self.config.add_docstrings:
            lines.append(f'    """Schema for updating {model.name}."""')
        lines.append("    model_config = ConfigDict(from_attributes=True)")
        lines.append("")
        for field_def in self._get_update_fields(model):
            lines.append(f"    {field_def}")
        if not base_fields:
            lines.append("    pass")
        lines.append("")
        lines.append("")

        return "\n".join(lines)

    def _get_base_fields(self, model: ParsedModel) -> list[str]:
        """Get field definitions for base schema (includes all fields for Read)."""
        fields = []

        for field in model.fields:
            # Skip primary key
            if field.primary_key:
                continue

            # Skip M2M (handled separately)
            if field.relation_type == RelationType.MANY_TO_MANY:
                continue

            field_def = self._format_field(field, optional=False)
            if field_def:
                fields.append(field_def)

        return fields

    def _get_create_fields(self, model: ParsedModel) -> list[str]:
        """Get field definitions for create schema (excludes auto-generated fields)."""
        fields = []

        for field in model.fields:
            # Skip primary key
            if field.primary_key:
                continue

            # Skip M2M (handled separately)
            if field.relation_type == RelationType.MANY_TO_MANY:
                continue

            # Skip auto_now and auto_now_add fields (auto-generated by DB)
            if field.auto_now or field.auto_now_add:
                continue

            field_def = self._format_field(field, optional=False, for_create=True)
            if field_def:
                fields.append(field_def)

        return fields

    def _get_update_fields(self, model: ParsedModel) -> list[str]:
        """Get field definitions for update schema (all optional)."""
        fields = []

        for field in model.fields:
            # Skip primary key
            if field.primary_key:
                continue

            # Skip M2M
            if field.relation_type == RelationType.MANY_TO_MANY:
                continue

            field_def = self._format_field(field, optional=True)
            if field_def:
                fields.append(field_def)

        return fields

    def _format_field(self, field: ParsedField, optional: bool = False, for_create: bool = False) -> str:
        """Format a single field for schema."""
        python_type = self.type_mapper.get_python_type(field)

        # For FK fields, use the ID column name
        name = field.name

        # Check if this is a blank string field
        is_string_field = field.django_type in (
            'CharField', 'TextField', 'SlugField', 'EmailField', 'URLField', 'FilePathField'
        )
        is_blank_string = field.blank and is_string_field

        # Make optional for update schema
        if optional:
            if not python_type.startswith("Optional"):
                python_type = f"Optional[{python_type}]"
            return f"{name}: {python_type} = None"

        # For create schema - blank string fields get default empty string
        if for_create and is_blank_string and not field.nullable:
            if not python_type.startswith("Optional"):
                python_type = f"Optional[{python_type}]"
            return f'{name}: {python_type} = ""'

        # For base/create schema
        if field.nullable:
            return f"{name}: {python_type} = None"

        # blank=True with null=True
        if field.blank and field.nullable:
            return f"{name}: {python_type} = None"

        return f"{name}: {python_type}"

    def _build_imports(self) -> str:
        """Build imports section."""
        lines = [
            "from typing import Optional, List, Any",
            "",
            "from pydantic import BaseModel, ConfigDict, Field",
        ]

        # Add datetime imports if needed
        datetime_types = []
        for module, name in self.type_mapper.imports:
            if module == "datetime":
                datetime_types.append(name)

        if datetime_types:
            lines.insert(0, f"from datetime import {', '.join(sorted(set(datetime_types)))}")

        # Add decimal, uuid if needed
        for module, name in self.type_mapper.imports:
            if module in ("decimal", "uuid"):
                lines.insert(0, f"from {module} import {name}")

        return "\n".join(lines)
