"""Implementation of the `strura gen:model` command."""

import re
from pathlib import Path
import typer

from strura.templates.gen_model_templates import (
    schema_template,
    model_template,
    data_dictionary_template,
)

# Regex for valid names: lowercase snake_case
_VALID_NAME_RE = re.compile(r"^[a-z][a-z0-9_]*$")

def _to_pascal_case(name: str) -> str:
    return "".join(word.capitalize() for word in name.split("_"))

def _validate_context(context: str) -> Path:
    cwd = Path.cwd()
    contexts_dir = cwd / "app" / "contexts"
    if not contexts_dir.is_dir():
        typer.echo("Error: Directory 'app/contexts/' not found. Run 'strura init' first.", err=True)
        raise typer.Exit(code=1)

    context_dir = contexts_dir / context
    if not context_dir.is_dir():
        typer.echo(f"Context '{context}' not found. Creating it...")
        from strura.commands.gen_context import gen_context
        gen_context(context)
        
    return context_dir

def _get_sa_type(py_type: str) -> str:
    mapping = {
        "string": "String",
        "int": "Integer",
        "float": "Float",
        "bool": "Boolean",
        "datetime": "DateTime(timezone=True)",
        "date": "Date",
        "uuid": "UUID(as_uuid=True)",
    }
    return mapping.get(py_type.lower(), "String")

def _get_py_type(py_type: str) -> str:
    mapping = {
        "string": "str",
        "int": "int",
        "float": "float",
        "bool": "bool",
        "datetime": "datetime",
        "date": "date",
        "uuid": "UUID",
    }
    return mapping.get(py_type.lower(), "str")

def _update_registry_schema(context_dir: Path, model_name: str) -> None:
    schema_path = context_dir / "infrastructure" / "database" / "schema.py"
    if not schema_path.exists():
        return

    content = schema_path.read_text(encoding="utf-8")
    new_line = f"    {model_name.upper()} = \"{model_name}\"\n"
    
    # Simple strategy: append before the last 'pass' or at the end of the class
    if "pass" in content:
        content = content.replace("    pass", new_line)
    else:
        # Append before the last line if it's empty or similar
        lines = content.splitlines()
        lines.append(new_line)
        content = "\n".join(lines)
        
    schema_path.write_text(content, encoding="utf-8")

def _update_registry_init_tables(context_dir: Path, context: str, model_name: str, pascal_name: str) -> None:
    init_tables_path = context_dir / "infrastructure" / "database" / "init_tables.py"
    if not init_tables_path.exists():
        return

    content = init_tables_path.read_text(encoding="utf-8")
    import_line = f"from app.contexts.{context}.infrastructure.database.models.{model_name} import {pascal_name}\n"
    
    content += import_line
    init_tables_path.write_text(content, encoding="utf-8")

def _update_erd(context_dir: Path, pascal_name: str, fields: list) -> None:
    erd_path = context_dir / "infrastructure" / "database" / "models" / "ERD.md"
    if not erd_path.exists():
        return

    content = erd_path.read_text(encoding="utf-8")
    
    # Construct the entity mermaid string
    entity_str = f"    {pascal_name.upper()} {{\n"
    for field in fields:
        # Simplified types for Mermaid
        f_type = field["py_type"].lower()
        if f_type == "uuid":
            f_type = "int" # Often represented as int/uuid in diagrams
        
        f_name = field["name"]
        is_pk = " PK" if field["constraints"].get("primary_key") else ""
        entity_str += f"        {f_type} {f_name}{is_pk}\n"
    entity_str += "    }\n"

    # Append before the last ``` 
    if "```mermaid" in content:
        # Find the erDiagram block
        if "erDiagram" in content:
            # We insert it after erDiagram or before the closing backticks
            parts = content.rsplit("```", 1)
            if len(parts) == 2:
                # Check if entity already exists to avoid duplication
                if f"    {pascal_name.upper()} {{" in content:
                    return
                # Insert before the closing backticks
                new_content = parts[0] + entity_str + "```" + parts[1]
                erd_path.write_text(new_content, encoding="utf-8")

def gen_model(
    entity: str = typer.Argument(
        ...,
        metavar="<context>.<model_name>",
        help="Entity to generate in dot notation. Example: person.address",
    )
) -> None:
    """Generate a domain schema and database model for an entity."""
    if "." not in entity:
        typer.echo("Error: Invalid syntax. Use <context>.<model_name> (e.g., person.address).", err=True)
        raise typer.Exit(code=1)

    context, model_name = entity.split(".", 1)
    
    if not _VALID_NAME_RE.match(model_name):
        typer.echo(f"Error: Invalid model name '{model_name}'. Use snake_case.", err=True)
        raise typer.Exit(code=1)

    context_dir = _validate_context(context)
    pascal_name = _to_pascal_case(model_name)

    typer.echo(f"\nConstructing entity: {pascal_name} in context: {context}\n")

    # Interactive flow
    db_schema = typer.prompt("Postgres schema", default=context)
    
    fields = []
    typer.echo("\n--- Define Fields (Empty name to finish) ---")
    
    # Primary Key by default
    if typer.confirm("Add standard 'id' UUID Primary Key?", default=True):
        fields.append({
            "name": "id",
            "py_type": "UUID",
            "sa_type": "UUID(as_uuid=True)",
            "constraints": {"primary_key": True, "nullable": False}
        })

    while True:
        f_name = typer.prompt("Field name", default="", show_default=False)
        if not f_name:
            break
            
        f_type = typer.prompt("Type (string/int/float/bool/datetime/date/uuid)", default="string")
        f_nullable = typer.confirm("Nullable?", default=False)
        f_unique = typer.confirm("Unique?", default=False)
        
        fields.append({
            "name": f_name,
            "py_type": _get_py_type(f_type),
            "sa_type": _get_sa_type(f_type),
            "constraints": {
                "nullable": f_nullable,
                "unique": f_unique,
            }
        })

    # File Generation
    typer.echo("\nGenerating files...")

    # 1. Schema
    schema_dir = context_dir / "domain" / "schemas"
    schema_dir.mkdir(parents=True, exist_ok=True)
    (schema_dir / f"{model_name}.py").write_text(
        schema_template(context, model_name, pascal_name, fields), encoding="utf-8"
    )
    typer.echo(f"  Created: app/contexts/{context}/domain/schemas/{model_name}.py")

    # 2. Model
    model_dir = context_dir / "infrastructure" / "database" / "models"
    model_dir.mkdir(parents=True, exist_ok=True)
    (model_dir / f"{model_name}.py").write_text(
        model_template(context, model_name, pascal_name, db_schema, fields), encoding="utf-8"
    )
    typer.echo(f"  Created: app/contexts/{context}/infrastructure/database/models/{model_name}.py")

    # 3. Documentation
    doc_dir = context_dir / "docs" / "entities"
    doc_dir.mkdir(parents=True, exist_ok=True)
    (doc_dir / f"{model_name}.md").write_text(
        data_dictionary_template(context, model_name, pascal_name, fields), encoding="utf-8"
    )
    typer.echo(f"  Created: app/contexts/{context}/docs/entities/{model_name}.md")

    # 4. Registry Updates
    typer.echo("\nUpdating registries...")
    _update_registry_schema(context_dir, model_name)
    _update_registry_init_tables(context_dir, context, model_name, pascal_name)
    _update_erd(context_dir, pascal_name, fields)
    typer.echo("  Updated: infrastructure/database/schema.py")
    typer.echo("  Updated: infrastructure/database/init_tables.py")
    typer.echo("  Updated: infrastructure/database/models/ERD.md")

    typer.echo(f"\nDone! Entity '{pascal_name}' is ready.")
