"""Schema management commands."""

from typing import Annotated

import typer

from kameleondb.cli.context import CLIContext
from kameleondb.cli.output import OutputFormatter
from kameleondb.cli.parsing import parse_field_spec, read_json_file

# Create schema subcommand group
app = typer.Typer(help="Manage entity schemas")


@app.command("list")
def schema_list(ctx: typer.Context) -> None:
    """List all entities in the database."""
    cli_ctx: CLIContext = ctx.obj
    formatter = OutputFormatter(cli_ctx.json_output)

    try:
        db = cli_ctx.get_db()
        entities = db.list_entities()

        if cli_ctx.json_output:
            formatter.print_data(entities)
        else:
            # Get full schema info for rich table
            db.describe()
            table_data = []
            for entity_name in entities:
                entity_info = db.describe_entity(entity_name)
                table_data.append(
                    {
                        "Name": entity_info.name,
                        "Fields": len(entity_info.fields),
                        "Records": entity_info.record_count or 0,
                        "Storage": entity_info.storage_mode,
                    }
                )

            formatter.print_table(
                f"Entities ({len(entities)} total)",
                table_data,
                ["Name", "Fields", "Records", "Storage"],
            )
    except Exception as e:
        formatter.print_error(e)
        raise typer.Exit(code=1)
    finally:
        cli_ctx.close()


@app.command("describe")
def schema_describe(
    ctx: typer.Context,
    entity_name: Annotated[str, typer.Argument(help="Entity name")],
) -> None:
    """Show detailed entity information."""
    cli_ctx: CLIContext = ctx.obj
    formatter = OutputFormatter(cli_ctx.json_output)

    try:
        db = cli_ctx.get_db()
        entity_info = db.describe_entity(entity_name)
        formatter.print_entity_info(entity_info)
    except Exception as e:
        formatter.print_error(e)
        raise typer.Exit(code=1)
    finally:
        cli_ctx.close()


@app.command("create")
def schema_create(
    ctx: typer.Context,
    name: Annotated[str, typer.Argument(help="Entity name (e.g., Contact, Order)")],
    fields: Annotated[
        list[str] | None,
        typer.Option(
            "--field",
            "-f",
            help="Field spec: name:type[:modifier]. Can be repeated.",
        ),
    ] = None,
    from_file: Annotated[
        str | None,
        typer.Option(
            "--from-file",
            help="Load schema from JSON file",
        ),
    ] = None,
    description: Annotated[
        str | None,
        typer.Option("--description", "-d", help="Entity description"),
    ] = None,
    created_by: Annotated[
        str | None,
        typer.Option("--created-by", help="Creator identifier for audit trail"),
    ] = None,
) -> None:
    """Create a new entity with fields.

    Examples:

        # Inline fields
        kameleondb schema create Contact --field "name:string:required" --field "email:string:unique"

        # From JSON file
        kameleondb schema create Contact --from-file schema.json
    """
    cli_ctx: CLIContext = ctx.obj
    formatter = OutputFormatter(cli_ctx.json_output)

    try:
        db = cli_ctx.get_db()

        # Parse field specs or load from file
        parsed_fields = []
        entity_name = name
        entity_description = description

        if from_file:
            # Load from JSON file
            schema_data = read_json_file(from_file)
            entity_name = schema_data.get("name", name)
            entity_description = schema_data.get("description", description)
            parsed_fields = schema_data.get("fields", [])
        elif fields:
            # Parse inline field specs
            parsed_fields = [parse_field_spec(spec) for spec in fields]

        # Create entity
        entity = db.create_entity(
            name=entity_name,
            fields=parsed_fields if parsed_fields else None,
            description=entity_description,
            created_by=created_by,
            if_not_exists=False,
        )

        formatter.print_success(
            f"Entity '{entity_name}' created",
            {
                "name": entity.name,
                "fields": len(parsed_fields) if parsed_fields else 0,
            },
        )
    except Exception as e:
        formatter.print_error(e)
        raise typer.Exit(code=1)
    finally:
        cli_ctx.close()


@app.command("drop")
def schema_drop(
    ctx: typer.Context,
    entity_name: Annotated[str, typer.Argument(help="Entity name")],
    reason: Annotated[
        str | None,
        typer.Option("--reason", help="Reason for dropping (audit trail)"),
    ] = None,
    force: Annotated[
        bool,
        typer.Option("--force", help="Skip confirmation prompt"),
    ] = False,
    created_by: Annotated[
        str | None,
        typer.Option("--created-by", help="Creator identifier for audit trail"),
    ] = None,
) -> None:
    """Drop an entity (soft delete)."""
    cli_ctx: CLIContext = ctx.obj
    formatter = OutputFormatter(cli_ctx.json_output)

    # Confirmation prompt
    if not force and not cli_ctx.json_output:
        confirm = typer.confirm(f"Are you sure you want to drop entity '{entity_name}'?")
        if not confirm:
            typer.echo("Cancelled.")
            raise typer.Exit(code=0)

    try:
        db = cli_ctx.get_db()
        success = db.drop_entity(
            name=entity_name,
            created_by=created_by,
            reason=reason,
        )

        if success:
            formatter.print_success(f"Entity '{entity_name}' dropped")
        else:
            formatter.print_error(Exception(f"Failed to drop entity '{entity_name}'"))
            raise typer.Exit(code=1)
    except Exception as e:
        formatter.print_error(e)
        raise typer.Exit(code=1)
    finally:
        cli_ctx.close()


@app.command("alter")
def schema_alter(
    ctx: typer.Context,
    entity_name: Annotated[str, typer.Argument(help="Entity name")],
    add: Annotated[
        list[str] | None,
        typer.Option("--add", "-a", help="Add field: name:type[:modifier] (repeatable)"),
    ] = None,
    drop: Annotated[
        list[str] | None,
        typer.Option("--drop", "-d", help="Drop field by name (repeatable)"),
    ] = None,
    rename: Annotated[
        list[str] | None,
        typer.Option("--rename", "-r", help="Rename field: old_name:new_name (repeatable)"),
    ] = None,
    reason: Annotated[
        str | None,
        typer.Option("--reason", help="Reason for change (audit trail)"),
    ] = None,
    created_by: Annotated[
        str | None,
        typer.Option("--created-by", help="Creator identifier for audit trail"),
    ] = None,
    force: Annotated[
        bool,
        typer.Option("--force", help="Skip confirmation for drops"),
    ] = False,
) -> None:
    """Alter an entity's schema (add, drop, rename fields).

    Consolidates schema evolution operations. Changes apply in order: add -> rename -> drop.

    Examples:

        # Add a field
        kameleondb schema alter Contact --add "phone:string"

        # Add multiple fields
        kameleondb schema alter Contact --add "phone:string:indexed" --add "score:int"

        # Drop a field
        kameleondb schema alter Contact --drop legacy_field --force

        # Rename a field
        kameleondb schema alter Contact --rename "old_name:new_name"

        # Combined operations
        kameleondb schema alter Contact --add "phone:string" --drop legacy --reason "Cleanup"
    """
    cli_ctx: CLIContext = ctx.obj
    formatter = OutputFormatter(cli_ctx.json_output)

    # Check if any operation specified
    if not add and not drop and not rename:
        formatter.print_error(Exception("No operations specified. Use --add, --drop, or --rename"))
        raise typer.Exit(code=1)

    # Confirmation for drops
    if drop and not force and not cli_ctx.json_output:
        confirm = typer.confirm(
            f"Are you sure you want to drop field(s) {drop} from '{entity_name}'?"
        )
        if not confirm:
            typer.echo("Cancelled.")
            raise typer.Exit(code=0)

    try:
        db = cli_ctx.get_db()
        entity = db.entity(entity_name)

        # Parse add fields
        add_fields = None
        if add:
            add_fields = []
            for spec in add:
                field = parse_field_spec(spec)
                add_fields.append(
                    {
                        "name": field["name"],
                        "type": field.get("type", "string"),
                        "required": field.get("required", False),
                        "unique": field.get("unique", False),
                        "indexed": field.get("indexed", False),
                        "default": field.get("default"),
                    }
                )

        # Parse renames
        rename_fields = None
        if rename:
            rename_fields = {}
            for spec in rename:
                parts = spec.split(":")
                if len(parts) != 2:
                    raise ValueError(f"Invalid rename spec '{spec}'. Use old_name:new_name")
                rename_fields[parts[0]] = parts[1]

        # Apply alterations
        info = entity.alter(
            add_fields=add_fields,
            drop_fields=drop,
            rename_fields=rename_fields,
            modify_fields=None,
            created_by=created_by,
            reason=reason,
        )

        # Build summary
        changes = []
        if add:
            changes.append(f"added {len(add)} field(s)")
        if rename:
            changes.append(f"renamed {len(rename)} field(s)")
        if drop:
            changes.append(f"dropped {len(drop)} field(s)")

        formatter.print_success(
            f"Altered '{entity_name}': {', '.join(changes)}",
            {"entity": entity_name, "fields": len(info.fields)},
        )
    except Exception as e:
        formatter.print_error(e)
        raise typer.Exit(code=1)
    finally:
        cli_ctx.close()


@app.command("add-relationship")
def schema_add_relationship(
    ctx: typer.Context,
    source_entity: Annotated[str, typer.Argument(help="Source entity (has the FK)")],
    target_entity: Annotated[str, typer.Argument(help="Target entity (being referenced)")],
    name: Annotated[
        str | None,
        typer.Option("--name", "-n", help="Relationship name (default: target entity lowercase)"),
    ] = None,
    on_delete: Annotated[
        str,
        typer.Option(
            "--on-delete",
            help="Action when target is deleted: CASCADE, SET_NULL, RESTRICT",
        ),
    ] = "SET_NULL",
    fk_field: Annotated[
        str | None,
        typer.Option("--fk-field", help="Foreign key field name (auto-generated if not provided)"),
    ] = None,
    description: Annotated[
        str | None,
        typer.Option("--description", "-d", help="Relationship description"),
    ] = None,
    created_by: Annotated[
        str | None,
        typer.Option("--created-by", help="Creator identifier for audit trail"),
    ] = None,
    reason: Annotated[
        str | None,
        typer.Option("--reason", help="Reason for change (audit trail)"),
    ] = None,
) -> None:
    """Create a many-to-one relationship between entities.

    Creates a foreign key relationship where the source entity references
    the target entity.

    Examples:

        # Order belongs to Customer (SET_NULL on delete)
        kameleondb schema add-relationship Order Customer

        # Comment references Post (CASCADE delete)
        kameleondb schema add-relationship Comment Post --on-delete CASCADE

        # Employee reports to Manager (RESTRICT delete)
        kameleondb schema add-relationship Employee Employee --name manager --on-delete RESTRICT
    """
    cli_ctx: CLIContext = ctx.obj
    formatter = OutputFormatter(cli_ctx.json_output)

    try:
        db = cli_ctx.get_db()

        # Default name is lowercase target entity
        rel_name = name or target_entity.lower()

        # Use schema engine directly
        relationship = db._schema_engine.add_relationship(
            source_entity_name=source_entity,
            name=rel_name,
            target_entity_name=target_entity,
            relationship_type="many_to_one",
            foreign_key_field=fk_field,
            on_delete=on_delete.upper(),
            description=description,
            created_by=created_by,
            reason=reason,
        )

        formatter.print_success(
            f"Relationship '{rel_name}' created: {source_entity} → {target_entity}",
            {
                "name": rel_name,
                "source": source_entity,
                "target": target_entity,
                "on_delete": on_delete.upper(),
                "fk_field": relationship.foreign_key_field,
            },
        )
    except Exception as e:
        formatter.print_error(e)
        raise typer.Exit(code=1)
    finally:
        cli_ctx.close()


@app.command("add-m2m")
def schema_add_m2m(
    ctx: typer.Context,
    source_entity: Annotated[str, typer.Argument(help="Source entity")],
    target_entity: Annotated[str, typer.Argument(help="Target entity")],
    name: Annotated[
        str | None,
        typer.Option("--name", "-n", help="Relationship name (auto-generated if not provided)"),
    ] = None,
    description: Annotated[
        str | None,
        typer.Option("--description", "-d", help="Relationship description"),
    ] = None,
    created_by: Annotated[
        str | None,
        typer.Option("--created-by", help="Creator identifier for audit trail"),
    ] = None,
    reason: Annotated[
        str | None,
        typer.Option("--reason", help="Reason for change (audit trail)"),
    ] = None,
) -> None:
    """Create a many-to-many relationship between entities.

    Creates a junction table to link records from both entities.
    Use 'data link' and 'data unlink' commands to manage links.

    Examples:

        # Product has many Tags, Tag has many Products
        kameleondb schema add-m2m Product Tag

        # Custom relationship name
        kameleondb schema add-m2m Student Course --name enrollments
    """
    cli_ctx: CLIContext = ctx.obj
    formatter = OutputFormatter(cli_ctx.json_output)

    try:
        db = cli_ctx.get_db()

        # Auto-generate name if not provided
        rel_name = name or f"{source_entity.lower()}_{target_entity.lower()}"

        # Create M2M relationship
        db._schema_engine.add_relationship(
            source_entity_name=source_entity,
            name=rel_name,
            target_entity_name=target_entity,
            relationship_type="many_to_many",
            on_delete="CASCADE",  # M2M always cascades junction entries
            description=description,
            created_by=created_by,
            reason=reason,
        )

        formatter.print_success(
            f"Many-to-many relationship '{rel_name}' created: {source_entity} <-> {target_entity}",
            {
                "name": rel_name,
                "source": source_entity,
                "target": target_entity,
                "type": "many_to_many",
            },
        )
    except Exception as e:
        formatter.print_error(e)
        raise typer.Exit(code=1)
    finally:
        cli_ctx.close()


@app.command("info")
def schema_info(
    ctx: typer.Context,
    entity_name: Annotated[
        str | None,
        typer.Argument(help="Entity name (optional, shows all if not specified)"),
    ] = None,
) -> None:
    """Get statistics about entities.

    Shows record counts, storage mode, query metrics, and optimization suggestions.

    Examples:

        # Stats for all entities
        kameleondb schema stats

        # Stats for specific entity
        kameleondb schema stats Contact
    """
    cli_ctx: CLIContext = ctx.obj
    formatter = OutputFormatter(cli_ctx.json_output)

    try:
        db = cli_ctx.get_db()

        if entity_name:
            # Stats for specific entity
            stats = db.get_entity_stats(entity_name)
            data = stats.model_dump()

            if cli_ctx.json_output:
                formatter.print_data(data)
            else:
                # Rich table format
                formatter.print_table(
                    f"Stats for {entity_name}",
                    [
                        {"Metric": "Record Count", "Value": stats.record_count},
                        {"Metric": "Storage Mode", "Value": stats.storage_mode},
                        {"Metric": "Total Queries", "Value": stats.total_queries},
                        {
                            "Metric": "Avg Query Time (ms)",
                            "Value": f"{stats.avg_execution_time_ms:.2f}",
                        },
                        {
                            "Metric": "Max Query Time (ms)",
                            "Value": f"{stats.max_execution_time_ms:.2f}",
                        },
                        {"Metric": "Total Rows Returned", "Value": stats.total_rows_returned},
                        {"Metric": "JOINs (24h)", "Value": stats.join_count_24h},
                        {"Metric": "Suggestion", "Value": stats.suggestion or "None"},
                    ],
                    ["Metric", "Value"],
                )
        else:
            # Stats for all entities
            entities = db.list_entities()
            all_stats = []

            for name in entities:
                stats = db.get_entity_stats(name)
                all_stats.append(stats.model_dump())

            if cli_ctx.json_output:
                formatter.print_data(all_stats)
            else:
                table_data = [
                    {
                        "Entity": s["entity_name"],
                        "Records": s["record_count"],
                        "Storage": s["storage_mode"],
                        "Queries": s["total_queries"],
                        "Avg Time (ms)": f"{s['avg_execution_time_ms']:.2f}",
                    }
                    for s in all_stats
                ]
                formatter.print_table(
                    f"Entity Statistics ({len(entities)} entities)",
                    table_data,
                    ["Entity", "Records", "Storage", "Queries", "Avg Time (ms)"],
                )
    except Exception as e:
        formatter.print_error(e)
        raise typer.Exit(code=1)
    finally:
        cli_ctx.close()


@app.command("context")
def schema_context(
    ctx: typer.Context,
    entities: Annotated[
        list[str] | None,
        typer.Option("--entity", "-e", help="Specific entities (default: all)"),
    ] = None,
    include_examples: Annotated[
        bool,
        typer.Option("--examples/--no-examples", help="Include example queries"),
    ] = True,
    include_relationships: Annotated[
        bool,
        typer.Option("--relationships/--no-relationships", help="Include relationships"),
    ] = True,
) -> None:
    """Output LLM-ready schema context.

    This command generates comprehensive schema information optimized
    for LLM SQL query generation.
    """
    cli_ctx: CLIContext = ctx.obj
    formatter = OutputFormatter(True)  # Always output as JSON

    try:
        db = cli_ctx.get_db()
        context = db.get_schema_context(
            entities=entities,
            include_examples=include_examples,
            include_relationships=include_relationships,
        )
        formatter.print_data(context)
    except Exception as e:
        formatter.print_error(e)
        raise typer.Exit(code=1)
    finally:
        cli_ctx.close()
