import typer
import warnings
from rich.console import Console
from typing_extensions import Annotated
from typing import Optional


from cruxctl.command_groups.bigsnow.bigquery.bigquery_orgs import BigqueryOrgs
from cruxctl.command_groups.bigsnow.databricks.databricks_orgs import DatabricksOrgs
from cruxctl.command_groups.bigsnow.snowflake.snowflake_orgs import SnowflakeOrgs

app = typer.Typer()
console = Console()
error_console = Console(stderr=True)
warnings.filterwarnings("ignore")


def get_cls(ctx_obj):
    instance = ctx_obj.get("instance")

    if instance.lower() == "snowflake":
        return SnowflakeOrgs()
    elif instance.lower() == "bigquery":
        return BigqueryOrgs()
    elif instance.lower() == "databricks":
        return DatabricksOrgs()
    else:
        typer.echo(f"Unknown instance type: {instance}")
        raise typer.Exit(code=1)


@app.command("list-all")
def list_all(
    ctx: typer.Context,
    unique: Annotated[
        bool,
        typer.Option(
            "--unique",
            help="Show only unique organizations without accounts",
            is_flag=True,
        ),
    ] = False,
):
    instance_cls = get_cls(ctx.obj)
    instance_cls.list_all(unique=unique)


@app.command("list")
def list(
    ctx: typer.Context,
    org_id: Annotated[
        Optional[str], typer.Option("--org_id", "-org_id", help="Pass in the org id")
    ] = None,
    org_name: Annotated[
        Optional[str],
        typer.Option(
            "--org_name",
            "-org_name",
            help="Search organization by name (case-insensitive substring match)",
        ),
    ] = None,
):
    instance_cls = get_cls(ctx.obj)
    instance_cls.list(org_id=org_id, org_name=org_name)


@app.command("add")
def add(
    ctx: typer.Context,
    org_id: Annotated[
        str, typer.Option("--org_id", "-org_id", help="Pass in the org id")
    ] = None,
):
    instance_cls = get_cls(ctx.obj)
    instance_cls.add(org_id)


@app.command("delete")
def delete(
    ctx: typer.Context,
    org_id: Annotated[
        str, typer.Option("--org_id", "-org_id", help="Pass in the org id")
    ] = None,
):
    instance_cls = get_cls(ctx.obj)
    instance_cls.delete(org_id)


@app.command("orgs-add")
def orgs_add(
    ctx: typer.Context,
    org_id: Annotated[
        str, typer.Option("--org_id", "-org_id", help="Pass in the org id")
    ] = None,
    recipient_id: Annotated[
        str,
        typer.Option(
            "--recipient_id", "-recipient_id", help="Pass in the recipient id"
        ),
    ] = None,
):
    instance_cls = get_cls(ctx.obj)
    instance_cls.orgs_add(org_id, recipient_id)


@app.command("orgs-delete")
def orgs_delete(
    ctx: typer.Context,
    org_id: Annotated[
        str, typer.Option("--org_id", "-org_id", help="Pass in the org id")
    ] = None,
    recipient_name: Annotated[
        str,
        typer.Option(
            "--recipient_name", "-recipient_name", help="Pass in the recipient name"
        ),
    ] = None,
):
    instance_cls = get_cls(ctx.obj)
    instance_cls.orgs_delete(org_id, recipient_name)


@app.command("accounts-list")
def accounts_list(
    ctx: typer.Context,
    org_id: Annotated[
        str, typer.Option("--org_id", "-org_id", help="Pass in the org id")
    ] = None,
):
    instance_cls = get_cls(ctx.obj)
    instance_cls.accounts_list(org_id)


@app.command("accounts-add")
def accounts_add(
    ctx: typer.Context,
    org_id: Annotated[
        str, typer.Option("--org_id", "-org_id", help="Pass in the org id")
    ] = None,
    locator: Annotated[
        str, typer.Option("--locator", "-locator", help="Pass in the locator account")
    ] = None,
    acct_id: Annotated[
        str, typer.Option("--acct_id", "-acct_id", help="Pass in the account id")
    ] = None,
    client_schema: Annotated[
        str,
        typer.Option(
            "--client_schema", "-client_schema", help="Pass in the client_schema"
        ),
    ] = None,
):
    instance_cls = get_cls(ctx.obj)
    instance_cls.accounts_add(org_id, locator, acct_id, client_schema)


@app.command("accounts-delete")
def accounts_delete(
    ctx: typer.Context,
    org_id: Annotated[
        str, typer.Option("--org_id", "-org_id", help="Pass in the org id")
    ] = None,
    locator: Annotated[
        str, typer.Option("--locator", "-locator", help="Pass in the locator account")
    ] = None,
    acct_id: Annotated[
        str, typer.Option("--acct_id", "-acct_id", help="Pass in the account id")
    ] = None,
):
    instance_cls = get_cls(ctx.obj)
    instance_cls.accounts_delete(org_id, locator, acct_id)


@app.command("entitlements-list")
def entitlements_list(
    ctx: typer.Context,
    org_id: Annotated[
        str, typer.Option("--org_id", "-org_id", help="Pass in the org id")
    ] = None,
):

    instance_cls = get_cls(ctx.obj)
    instance_cls.entitlements_list(org_id)


@app.command("entitlements-add")
def entitlements_add(
    ctx: typer.Context,
    org_id: Annotated[
        str, typer.Option("--org_id", "-org_id", help="Pass in the org id")
    ] = None,
    cdu_id: Annotated[
        str, typer.Option("--cdu_id", "-cdu_id", help="Pass in the cdu id")
    ] = None,
):

    instance_cls = get_cls(ctx.obj)
    instance_cls.entitlements_add(org_id, cdu_id)


@app.command("entitlements-delete")
def entitlements_delete(
    ctx: typer.Context,
    org_id: Annotated[
        str, typer.Option("--org_id", "-org_id", help="Pass in the org id")
    ] = None,
    cdu_id: Annotated[
        str, typer.Option("--cdu_id", "-cdu_id", help="Pass in the cdu id")
    ] = None,
):
    instance_cls = get_cls(ctx.obj)
    instance_cls.entitlements_delete(org_id, cdu_id)


@app.command("entitlements-sync")
def entitlements_sync(
    ctx: typer.Context,
    org_id: Annotated[
        str, typer.Option("--org_id", "-org_id", help="Pass in the org id")
    ] = None,
    dry_run: Annotated[
        bool,
        typer.Option(
            "--dry-run",
            "-d",
            help="Preview changes without making them",
            is_flag=True,
        ),
    ] = False,
):
    """Sync all entitlements for an organization to SERVICE_RESTRICTIONS table"""
    instance_cls = get_cls(ctx.obj)
    instance_cls.entitlements_sync(org_id, dry_run)


@app.command("provision-list")
def provision_list(
    ctx: typer.Context,
    org_id: Annotated[
        str, typer.Option("--org_id", "-org_id", help="Pass in the org id")
    ] = None,
    region: Annotated[
        str, typer.Option("--region", "-region", help="Pass in the region")
    ] = None,
):

    instance_cls = get_cls(ctx.obj)
    instance_cls.provision_list(org_id, region)


@app.command("provision-add")
def provision_add(
    ctx: typer.Context,
    org_id: Annotated[
        str, typer.Option("--org_id", "-org_id", help="Pass in the org id")
    ] = None,
    view_name: Annotated[
        str, typer.Option("--view_name", "-view_name", help="Pass in the view name")
    ] = None,
    region: Annotated[
        str, typer.Option("--region", "-region", help="Pass in the region")
    ] = None,
):
    instance_cls = get_cls(ctx.obj)
    instance = ctx.obj.get("instance").lower()

    # Route based on instance type
    if instance == "bigquery":
        # BigQuery doesn't use region parameter
        if not org_id or not view_name:
            print("Error: BigQuery requires --org_id and --view_name")
            raise typer.Exit(1)
        instance_cls.provision_add(org_id=org_id, view_name=view_name)

    elif instance == "snowflake":
        # Snowflake requires all three parameters
        if not org_id or not view_name or not region:
            print("Error: Snowflake requires --org_id, --view_name, and --region")
            raise typer.Exit(1)
        instance_cls.provision_add(org_id, view_name, region)

    elif instance == "databricks":
        # Databricks requires org_id and view_name
        if not org_id or not view_name:
            print("Error: Databricks requires --org_id and --view_name")
            raise typer.Exit(1)
        instance_cls.provision_add(org_id, view_name)

    else:
        typer.echo(f"Unknown instance type: {instance}")
        raise typer.Exit(code=1)


@app.command("provision-delete")
def provision_delete(
    ctx: typer.Context,
    org_id: Annotated[
        str, typer.Option("--org_id", "-org_id", help="Pass in the org id")
    ] = None,
    view_name: Annotated[
        str, typer.Option("--view_name", "-view_name", help="Pass in the org id")
    ] = None,
    region: Annotated[
        str, typer.Option("--region", "-region", help="Pass in the region")
    ] = None,
):

    instance_cls = get_cls(ctx.obj)
    instance_cls.provision_delete(org_id, view_name, region)


@app.command("share-list")
def share_list(
    ctx: typer.Context,
    org_id: Annotated[
        str, typer.Option("--org_id", "-org_id", help="Pass in the org id")
    ] = None,
    region: Annotated[
        str, typer.Option("--region", "-region", help="Pass in the region")
    ] = None,
):

    instance_cls = get_cls(ctx.obj)
    instance_cls.share_list(org_id, region)


@app.command("share-add")
def share_add(
    ctx: typer.Context,
    org_id: Annotated[
        str, typer.Option("--org_id", "-org_id", help="Pass in the org id")
    ] = None,
    view_name: Annotated[
        str, typer.Option("--view_name", "-view_name", help="Pass in the org id")
    ] = None,
    region: Annotated[
        str, typer.Option("--region", "-region", help="Pass in the region")
    ] = None,
):

    instance_cls = get_cls(ctx.obj)
    instance_cls.share_add(org_id, view_name, region)


@app.command("share-delete")
def share_delete(
    ctx: typer.Context,
    org_id: Annotated[
        str, typer.Option("--org_id", "-org_id", help="Pass in the org id")
    ] = None,
    view_name: Annotated[
        str, typer.Option("--view_name", "-view_name", help="Pass in the org id")
    ] = None,
    region: Annotated[
        str, typer.Option("--region", "-region", help="Pass in the region")
    ] = None,
):

    instance_cls = get_cls(ctx.obj)
    instance_cls.share_delete(org_id, view_name, region)


@app.command("entitlements-update")
def entitlements_update(
    ctx: typer.Context,
    org_id: Annotated[
        str, typer.Option("--org_id", "-org_id", help="Pass in the org id")
    ] = None,
    input_file: Annotated[
        str, typer.Option("--input", "-i", help="Path to CSV file for bulk update")
    ] = None,
):
    instance_cls = get_cls(ctx.obj)
    instance_cls.entitlements_update(org_id, input_file)


@app.command()
def provision_missing_views(
    org_id: Optional[str] = typer.Option(
        None, "--org_id", help="Organization ID (e.g., CXO12345)"
    )
):
    """List missing views for a specific organization"""
    if not org_id:
        print("Error: Organization ID is required")
        raise typer.Exit(1)

    bigquery_orgs = BigqueryOrgs()
    bigquery_orgs.provision_missing_views(org_id)


@app.command("wrangles-validate")
def wrangles_validate(
    ctx: typer.Context,
    sql_file: str = typer.Option(
        ..., "--sql_file", help="Path to SQL file containing wrangle definition"
    ),
    region: str = typer.Option(None, "--region", help="Region (US/EU/VPS)"),
):
    """
    Validate wrangle SQL by creating view in CRUX_WRANGLE_UAT schema.

    Example:
        cruxctl bigsnow --instance snowflake orgs wrangles-validate --sql_file wrangle.sql
    """
    instance_cls = get_cls(ctx.obj)
    instance_cls.wrangles_validate(sql_file, region)


@app.command("wrangles-add")
def wrangles_add(
    ctx: typer.Context,
    sql_file: str = typer.Option(
        ..., "--sql_file", help="Path to SQL file containing wrangle definition"
    ),
    org_id: str = typer.Option(..., "--org_id", help="Organization ID"),
    region: str = typer.Option(None, "--region", help="Region (US/EU/VPS)"),
    dry_run: bool = typer.Option(
        False, "--dry_run", help="Show what would be done without executing"
    ),
):
    """
    Create wrangle view in CRUX_WRANGLE (production) and grant to customer share.

    Example:
        cruxctl bigsnow --instance snowflake orgs wrangles-add --sql_file wrangle.sql
        --org_id CXO01010 --dry_run
    """
    instance_cls = get_cls(ctx.obj)
    instance_cls.wrangles_add(sql_file, org_id, region, dry_run)


@app.command("wrangles-list")
def wrangles_list(
    ctx: typer.Context,
    org_id: str = typer.Option(..., "--org_id", help="Organization ID"),
    region: str = typer.Option(None, "--region", help="Region (US/EU/VPS)"),
):
    """
    List all wrangle views for an organization.

    Example:
        cruxctl bigsnow --instance snowflake orgs wrangles-list --org_id CXO01010
    """
    instance_cls = get_cls(ctx.obj)
    instance_cls.wrangles_list(org_id, region)


@app.command("wrangles-share-add")
def wrangles_share_add(
    ctx: typer.Context,
    view_name: str = typer.Option(..., "--view_name", help="Wrangle view name"),
    org_id: str = typer.Option(..., "--org_id", help="Organization ID"),
    region: str = typer.Option(None, "--region", help="Region (US/EU/VPS)"),
):
    """
    Grant wrangle view to organization's share.

    Example:
        cruxctl bigsnow --instance snowflake orgs wrangles-share-add
        --view_name "CX09815--TABLE--DATASET" --org_id CXO01010
    """
    instance_cls = get_cls(ctx.obj)
    instance_cls.wrangles_share_add(view_name, org_id, region)


@app.command("wrangles-share-remove")
def wrangles_share_remove(
    ctx: typer.Context,
    view_name: str = typer.Option(..., "--view_name", help="Wrangle view name"),
    org_id: str = typer.Option(..., "--org_id", help="Organization ID"),
    region: str = typer.Option(None, "--region", help="Region (US/EU/VPS)"),
):
    """
    Revoke wrangle view from organization's share.

    Example:
        cruxctl bigsnow --instance snowflake orgs wrangles-share-remove
        --view_name "CX09815--TABLE--DATASET" --org_id CXO01010
    """
    instance_cls = get_cls(ctx.obj)
    instance_cls.wrangles_share_remove(view_name, org_id, region)


@app.command("wrangles-share-create")
def wrangles_share_create(
    ctx: typer.Context,
    org_id: str = typer.Option(..., "--org_id", help="Organization ID"),
    account_locator: str = typer.Option(
        None, "--account_locator", help="Customer's Snowflake account locator"
    ),
    region: str = typer.Option(None, "--region", help="Region (US/EU/VPS)"),
):
    """
    Create wrangle share for an organization.

    Example:
        cruxctl bigsnow --instance snowflake orgs wrangles-share-create
        --org_id CXO01010 --account_locator XY12345
    """
    instance_cls = get_cls(ctx.obj)
    instance_cls.wrangles_share_create(org_id, account_locator, region)
