"""Code generation commands."""

import asyncio
from pathlib import Path

import click


@click.group()
def generate() -> None:
    """Generate code from your FraiseQL schema."""


@generate.command()
@click.option(
    "--output",
    "-o",
    default="schema.graphql",
    help="Output file for GraphQL schema",
)
def schema(output: str) -> None:
    """Generate GraphQL schema from your FraiseQL types.

    This introspects all registered FraiseQL types and generates
    a complete GraphQL schema file.
    """
    click.echo("📄 Generating GraphQL schema...")

    try:
        # Import the app to get registered types
        import importlib.util

        spec = importlib.util.spec_from_file_location("main", "src/main.py")
        main_module = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(main_module)

        # Get the schema from the app
        if hasattr(main_module, "app"):
            from fraiseql.gql.schema_builder import SchemaRegistry

            registry = SchemaRegistry.get_instance()
            schema_str = registry.build_schema_string()

            # Write schema to file
            Path(output).write_text(schema_str)
            click.echo(f"✅ Schema written to {output}")
        else:
            click.echo("Error: No 'app' found in src/main.py", err=True)
            msg = "No 'app' found in src/main.py"
            raise click.ClickException(msg)

    except Exception as e:
        click.echo(f"Error generating schema: {e}", err=True)
        raise click.ClickException(str(e)) from e


@generate.command()
@click.argument("entity_name")
@click.option(
    "--table",
    help="Database table name (defaults to entity_name lowercase + 's')",
)
def migration(entity_name: str, table: str | None) -> None:
    """Generate a database migration for a FraiseQL type.

    Creates a SQL migration file that sets up the JSONB table
    structure needed for the specified entity.
    """
    if not table:
        table = f"{entity_name.lower()}s"

    timestamp = asyncio.run(get_timestamp())
    filename = f"migrations/{timestamp}_create_{table}.sql"

    migration_content = f"""-- Migration: Create {table} table for {entity_name} entity
-- Generated by FraiseQL CLI

-- Create table with JSONB data column
CREATE TABLE IF NOT EXISTS {table} (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    data JSONB NOT NULL DEFAULT '{{}}',
    created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
    deleted_at TIMESTAMPTZ
);

-- Create indexes
CREATE INDEX IF NOT EXISTS idx_{table}_data ON {table} USING gin(data);
CREATE INDEX IF NOT EXISTS idx_{table}_created_at ON {table}(created_at);
CREATE INDEX IF NOT EXISTS idx_{table}_deleted_at ON {table}(deleted_at) WHERE deleted_at IS NULL;

-- Create updated_at trigger
CREATE OR REPLACE FUNCTION update_{table}_updated_at()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = CURRENT_TIMESTAMP;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trigger_update_{table}_updated_at
    BEFORE UPDATE ON {table}
    FOR EACH ROW
    EXECUTE FUNCTION update_{table}_updated_at();

-- Create view for FraiseQL
CREATE OR REPLACE VIEW v_{table} AS
SELECT
    id,
    data,
    created_at,
    updated_at
FROM {table}
WHERE deleted_at IS NULL;

-- Grant permissions (adjust as needed)
-- GRANT SELECT ON v_{table} TO your_app_user;
-- GRANT INSERT, UPDATE, DELETE ON {table} TO your_app_user;
"""

    # Ensure migrations directory exists
    Path("migrations").mkdir(exist_ok=True)

    # Write migration file
    Path(filename).write_text(migration_content)

    click.echo(f"✅ Migration created: {filename}")
    click.echo("\nRun this migration with:")
    click.echo(f"  psql $DATABASE_URL -f {filename}")


@generate.command()
@click.argument("type_name")
def crud(type_name: str) -> None:
    """Generate CRUD mutations for a FraiseQL type.

    Creates create, update, and delete mutations for the specified type.
    """
    click.echo(f"🔧 Generating CRUD mutations for {type_name}...")

    mutations_content = f'''"""CRUD mutations for {type_name}."""

import fraiseql
from fraiseql import fraise_field
from fraiseql.mutations import CQRSRepository
from fraiseql.types.scalars import UUID

from ..types import {type_name}


@fraiseql.input
class Create{type_name}Input:
    """Input for creating a {type_name}."""
    # TODO: Add your fields here
    name: str = fraise_field(description="Name")


@fraiseql.input
class Update{type_name}Input:
    """Input for updating a {type_name}."""
    id: UUID = fraise_field(description="{type_name} ID")
    # TODO: Add your fields here
    name: str | None = fraise_field(description="Name")


@fraiseql.success
class {type_name}Success:
    """{type_name} mutation succeeded."""
    {type_name.lower()}: {type_name}
    message: str


@fraiseql.failure
class {type_name}Error:
    """{type_name} mutation failed."""
    message: str
    code: str


@fraiseql.result
class {type_name}Result:
    """{type_name} mutation result."""
    pass


@fraiseql.mutation
async def create_{type_name.lower()}(
    input: Create{type_name}Input,
    repository: CQRSRepository,
) -> {type_name}Result:
    """Create a new {type_name}."""
    try:
        # TODO: Implement your creation logic
        result = await repository.create("{type_name.lower()}s", input)
        return {type_name}Success(
            {type_name.lower()}=result,
            message="{type_name} created successfully"
        )
    except Exception as e:
        return {type_name}Error(
            message=str(e),
            code="CREATE_FAILED"
        )


@fraiseql.mutation
async def update_{type_name.lower()}(
    input: Update{type_name}Input,
    repository: CQRSRepository,
) -> {type_name}Result:
    """Update an existing {type_name}."""
    try:
        # TODO: Implement your update logic
        result = await repository.update("{type_name.lower()}s", input.id, input)
        return {type_name}Success(
            {type_name.lower()}=result,
            message="{type_name} updated successfully"
        )
    except Exception as e:
        return {type_name}Error(
            message=str(e),
            code="UPDATE_FAILED"
        )


@fraiseql.mutation
async def delete_{type_name.lower()}(
    id: UUID,
    repository: CQRSRepository,
) -> {type_name}Result:
    """Delete a {type_name}."""
    try:
        # TODO: Implement your deletion logic (soft delete recommended)
        await repository.delete("{type_name.lower()}s", id)
        return {type_name}Success(
            {type_name.lower()}=None,
            message="{type_name} deleted successfully"
        )
    except Exception as e:
        return {type_name}Error(
            message=str(e),
            code="DELETE_FAILED"
        )
'''

    # Ensure mutations directory exists
    Path("src/mutations").mkdir(parents=True, exist_ok=True)

    # Write mutations file
    filename = f"src/mutations/{type_name.lower()}_mutations.py"
    Path(filename).write_text(mutations_content)

    click.echo(f"✅ CRUD mutations created: {filename}")
    click.echo("\nDon't forget to:")
    click.echo("1. Import and register these mutations in your app")
    click.echo("2. Customize the input fields and logic")
    click.echo("3. Run migrations to create the database table")


async def get_timestamp():
    """Get current timestamp for migration filenames."""
    from datetime import datetime

    return datetime.now(datetime.UTC).strftime("%Y%m%d%H%M%S")
