import typer
from pathlib import Path

from typing import Optional

from ..entities.agent import AgentRepository
from ..entities.agent_template import AgentTemplateRepository
from ..entities.agent_function import AgentFunctionRepository
from ..entities.agent_environment import AgentEnvironmentRepository
from ..entities.agent_phone_number_assignment import AgentPhoneNumberAssignmentRepository
from ..entities.agent_secret import AgentSecretRepository
from ..entities.organization import OrganizationRepository
from ..entities.organization_phone_number import OrganizationPhoneNumberRepository
from ..entities.organization_secret import OrganizationSecretRepository
from ..entities.organization_telephony import OrganizationTelephonyRepository
from ..entities.user import get_current_user, get_effective_organization_id
from ..utils.agent_lock import load_agent_lock
from ..utils.config import (
    ERROR_STYLE,
    INFO_STYLE,
    NOT_AVAILABLE,
)
from ..utils.utils import console, print_table

app = typer.Typer(help="List resources by type.")


@app.command("agents")
def get_agents():
    """List all agents."""
    repo = AgentRepository()
    agents = repo.get()

    if not agents:
        console.print(f"[{INFO_STYLE}]No agents found.[/{INFO_STYLE}]")
        return

    headers = ["NAME", "ID", "DESCRIPTION"]
    rows = []
    for agent in agents:
        desc = agent.description or NOT_AVAILABLE
        if len(desc) > 40:
            desc = desc[:37] + "..."
        rows.append([
            agent.name or NOT_AVAILABLE,
            agent.id or NOT_AVAILABLE,
            desc,
        ])
    print_table(headers, rows)


@app.command("functions")
def get_functions(
    agent: str = typer.Argument(help="Agent name or ID")
):
    """List all functions for an agent."""
    # First resolve the agent
    agent_repo = AgentRepository()
    agent_entity = agent_repo.get_by_name_or_id(agent)

    if not agent_entity:
        console.print(f"[{ERROR_STYLE}]Agent not found: {agent}[/{ERROR_STYLE}]")
        raise typer.Exit(1)

    repo = AgentFunctionRepository(agent_entity.id)
    functions = repo.get()

    if not functions:
        console.print(f"[{INFO_STYLE}]No functions found for agent '{agent_entity.name}'.[/{INFO_STYLE}]")
        return

    headers = ["DISPLAY NAME", "NAME", "ID", "LANGUAGE", "STRATEGY", "MULTIFILE"]
    rows = []
    for func in functions:
        rows.append([
            func.display_name or NOT_AVAILABLE,
            func.name or NOT_AVAILABLE,
            func.id or NOT_AVAILABLE,
            func.language or NOT_AVAILABLE,
            func.strategy or NOT_AVAILABLE,
            str(func.is_multifile) if func.is_multifile is not None else NOT_AVAILABLE,
        ])
    print_table(headers, rows)


@app.command("environments")
def get_environments(
    agent: str = typer.Argument(help="Agent name or ID")
):
    """List all environments for an agent."""
    # First resolve the agent
    agent_repo = AgentRepository()
    agent_entity = agent_repo.get_by_name_or_id(agent)

    if not agent_entity:
        console.print(f"[{ERROR_STYLE}]Agent not found: {agent}[/{ERROR_STYLE}]")
        raise typer.Exit(1)

    repo = AgentEnvironmentRepository(agent_entity.id)
    environments = repo.get()

    if not environments:
        console.print(f"[{INFO_STYLE}]No environments found for agent '{agent_entity.name}'.[/{INFO_STYLE}]")
        return

    headers = ["NAME", "ID", "PHONE", "STT MODEL", "RECORDING", "DEBUG"]
    rows = []
    for env in environments:
        rows.append([
            env.name or NOT_AVAILABLE,
            env.id or NOT_AVAILABLE,
            env.phone_number or NOT_AVAILABLE,
            env.stt_model or NOT_AVAILABLE,
            "Yes" if env.recording_enabled else "No",
            "Yes" if env.is_debug else "No",
        ])
    print_table(headers, rows)


@app.command("assignments")
@app.command("assignment", hidden=True)
def get_assignments(
    agent: str = typer.Argument(help="Agent name or ID"),
):
    """List all phone number assignments for an agent."""
    # Resolve the agent
    agent_repo = AgentRepository()
    agent_entity = agent_repo.get_by_name_or_id(agent)

    if not agent_entity:
        console.print(f"[{ERROR_STYLE}]Agent not found: {agent}[/{ERROR_STYLE}]")
        raise typer.Exit(1)

    repo = AgentPhoneNumberAssignmentRepository(agent_entity.id)
    assignments = repo.get()

    if not assignments:
        console.print(f"[{INFO_STYLE}]No assignments found for agent '{agent_entity.name}'.[/{INFO_STYLE}]")
        return

    headers = ["PHONE NUMBER ID", "ENVIRONMENT ID", "ASSIGNMENT ID", "CREATED AT"]
    rows = []
    for assignment in assignments:
        rows.append([
            assignment.organization_phone_number_id or NOT_AVAILABLE,
            assignment.agent_environment_id or NOT_AVAILABLE,
            assignment.id or NOT_AVAILABLE,
            assignment.created_at or NOT_AVAILABLE,
        ])
    print_table(headers, rows)


def _print_secrets_table(title: str, secrets: list):
    """Helper to print a secrets table."""
    console.print(f"[{INFO_STYLE}]{title}[/{INFO_STYLE}]")
    headers = ["NAME", "ID", "CREATED AT", "UPDATED AT"]
    rows = []
    for secret in secrets:
        rows.append([
            secret.name or NOT_AVAILABLE,
            secret.id or NOT_AVAILABLE,
            secret.created_at or NOT_AVAILABLE,
            secret.updated_at or NOT_AVAILABLE,
        ])
    print_table(headers, rows)


@app.command("secrets")
@app.command("secret", hidden=True)
def get_secrets(
    agent: Optional[str] = typer.Option(None, "--agent", "-a", help="Agent name or ID")
):
    """List secrets. Shows organization secrets, plus agent secrets if in a voicerun project or --agent is specified."""
    # Get organization ID (from config override or user session)
    organization_id = get_effective_organization_id()
    if not organization_id:
        console.print(f"[{ERROR_STYLE}]Unable to determine organization. Please sign in or set organization ID with 'vr context set-org'.[/{ERROR_STYLE}]")
        raise typer.Exit(1)

    # Determine agent - from flag or from agent.lock
    agent_entity = None
    agent_id = None

    if agent:
        # Agent specified via flag
        agent_repo = AgentRepository()
        agent_entity = agent_repo.get_by_name_or_id(agent)
        if not agent_entity:
            console.print(f"[{ERROR_STYLE}]Agent not found: {agent}[/{ERROR_STYLE}]")
            raise typer.Exit(1)
        agent_id = agent_entity.id
    else:
        # Check for agent.lock in current directory
        voicerun_dir = Path.cwd() / ".voicerun"
        if voicerun_dir.exists():
            lock_data = load_agent_lock(voicerun_dir)
            if lock_data and "id" in lock_data:
                agent_id = lock_data["id"]
                # Fetch agent details for display
                agent_repo = AgentRepository()
                agent_entity = agent_repo.get_by_id(agent_id)

    # Get and display organization secrets
    org_repo = OrganizationSecretRepository()
    org_secrets = org_repo.get()

    if org_secrets:
        _print_secrets_table("Organization Secrets", org_secrets)
    else:
        console.print(f"[{INFO_STYLE}]No organization secrets found.[/{INFO_STYLE}]")

    # Get and display agent secrets if we have an agent
    if agent_id:
        console.print()  # Add spacing between tables
        agent_repo = AgentSecretRepository(agent_id)
        agent_secrets = agent_repo.get()

        agent_name = agent_entity.name if agent_entity else agent_id
        if agent_secrets:
            _print_secrets_table(f"Secrets for Agent: {agent_name}", agent_secrets)
        else:
            console.print(f"[{INFO_STYLE}]No secrets found for agent '{agent_name}'.[/{INFO_STYLE}]")


@app.command("phonenumbers")
@app.command("phonenumber", hidden=True)
def get_phone_numbers():
    """List organization phone numbers."""
    organization_id = get_effective_organization_id()
    if not organization_id:
        console.print(f"[{ERROR_STYLE}]Unable to determine organization. Please sign in or set organization ID with 'vr context set-org'.[/{ERROR_STYLE}]")
        raise typer.Exit(1)

    repo = OrganizationPhoneNumberRepository()
    phone_numbers = repo.get()

    if not phone_numbers:
        console.print(f"[{INFO_STYLE}]No phone numbers found.[/{INFO_STYLE}]")
        return

    headers = ["PHONE NUMBER", "FRIENDLY NAME", "ID", "AREA CODE", "COUNTRY CODE"]
    rows = []
    for pn in phone_numbers:
        rows.append([
            pn.phone_number or NOT_AVAILABLE,
            pn.friendly_name or NOT_AVAILABLE,
            pn.id or NOT_AVAILABLE,
            pn.area_code or NOT_AVAILABLE,
            pn.country_code or NOT_AVAILABLE,
        ])
    print_table(headers, rows)


@app.command("telephony")
@app.command("telephonies", hidden=True)
def get_telephony():
    """List organization telephony providers."""
    organization_id = get_effective_organization_id()
    if not organization_id:
        console.print(f"[{ERROR_STYLE}]Unable to determine organization. Please sign in or set organization ID with 'vr context set-org'.[/{ERROR_STYLE}]")
        raise typer.Exit(1)

    repo = OrganizationTelephonyRepository()
    providers = repo.get()

    if not providers:
        console.print(f"[{INFO_STYLE}]No telephony providers found.[/{INFO_STYLE}]")
        return

    headers = ["NAME", "ID", "PROVIDER TYPE"]
    rows = []
    for provider in providers:
        rows.append([
            provider.name or NOT_AVAILABLE,
            provider.id or NOT_AVAILABLE,
            provider.provider_type or NOT_AVAILABLE,
        ])
    print_table(headers, rows)


@app.command("templates")
@app.command("template", hidden=True)
def get_templates():
    """List available agent templates."""
    repo = AgentTemplateRepository()
    templates = repo.get()

    if not templates:
        console.print(f"[{INFO_STYLE}]No templates found.[/{INFO_STYLE}]")
        return

    headers = ["NAME", "ID", "CATEGORY", "DESCRIPTION", "PUBLIC"]
    rows = []
    for t in templates:
        desc = t.description or NOT_AVAILABLE
        if len(desc) > 40:
            desc = desc[:37] + "..."
        rows.append([
            t.name or NOT_AVAILABLE,
            t.id or NOT_AVAILABLE,
            t.category or NOT_AVAILABLE,
            desc,
            "Yes" if t.is_public else "No",
        ])
    print_table(headers, rows)


@app.command("organizations")
@app.command("organization", hidden=True)
@app.command("orgs", hidden=True)
def get_organizations():
    """List all organizations (admin only)."""
    user = get_current_user()
    if not user or not user.is_admin:
        console.print(f"[{ERROR_STYLE}]Unauthorized[/{ERROR_STYLE}]")
        raise typer.Exit(1)

    repo = OrganizationRepository()
    organizations = repo.get()

    if not organizations:
        console.print(f"[{INFO_STYLE}]No organizations found.[/{INFO_STYLE}]")
        return

    headers = ["NAME", "ID", "TIER", "AGENTS", "CREATED AT"]
    rows = []
    for org in organizations:
        rows.append([
            org.name or NOT_AVAILABLE,
            org.id or NOT_AVAILABLE,
            org.tier or NOT_AVAILABLE,
            str(org.agent_count) if org.agent_count is not None else NOT_AVAILABLE,
            org.created_at or NOT_AVAILABLE,
        ])
    print_table(headers, rows)


if __name__ == "__main__":
    app()
