import os

import click
import uvicorn

from nextcloud_mcp_server.config import (
    get_settings,
)
from nextcloud_mcp_server.observability import get_uvicorn_logging_config

from .app import get_app


@click.command()
@click.option(
    "--host", "-h", default="127.0.0.1", show_default=True, help="Server host"
)
@click.option(
    "--port", "-p", type=int, default=8000, show_default=True, help="Server port"
)
@click.option(
    "--log-level",
    "-l",
    default="info",
    show_default=True,
    type=click.Choice(["critical", "error", "warning", "info", "debug", "trace"]),
    help="Logging level",
)
@click.option(
    "--transport",
    "-t",
    default="streamable-http",
    show_default=True,
    type=click.Choice(["streamable-http", "http"]),
    help="MCP transport protocol",
)
@click.option(
    "--enable-app",
    "-e",
    multiple=True,
    type=click.Choice(
        ["notes", "tables", "webdav", "calendar", "contacts", "cookbook", "deck"]
    ),
    help="Enable specific Nextcloud app APIs. Can be specified multiple times. If not specified, all apps are enabled.",
)
@click.option(
    "--oauth/--no-oauth",
    default=None,
    help="Force OAuth mode (if enabled) or BasicAuth mode (if disabled). By default, auto-detected based on environment variables.",
)
@click.option(
    "--oauth-client-id",
    envvar="NEXTCLOUD_OIDC_CLIENT_ID",
    help="OAuth client ID (can also use NEXTCLOUD_OIDC_CLIENT_ID env var)",
)
@click.option(
    "--oauth-client-secret",
    envvar="NEXTCLOUD_OIDC_CLIENT_SECRET",
    help="OAuth client secret (can also use NEXTCLOUD_OIDC_CLIENT_SECRET env var)",
)
@click.option(
    "--mcp-server-url",
    envvar="NEXTCLOUD_MCP_SERVER_URL",
    default="http://localhost:8000",
    show_default=True,
    help="MCP server URL for OAuth callbacks (can also use NEXTCLOUD_MCP_SERVER_URL env var)",
)
@click.option(
    "--nextcloud-host",
    envvar="NEXTCLOUD_HOST",
    help="Nextcloud instance URL (can also use NEXTCLOUD_HOST env var)",
)
@click.option(
    "--nextcloud-username",
    envvar="NEXTCLOUD_USERNAME",
    help="Nextcloud username for BasicAuth (can also use NEXTCLOUD_USERNAME env var)",
)
@click.option(
    "--nextcloud-password",
    envvar="NEXTCLOUD_PASSWORD",
    help="Nextcloud password for BasicAuth (can also use NEXTCLOUD_PASSWORD env var)",
)
@click.option(
    "--oauth-scopes",
    envvar="NEXTCLOUD_OIDC_SCOPES",
    default="openid profile email notes:read notes:write calendar:read calendar:write todo:read todo:write contacts:read contacts:write cookbook:read cookbook:write deck:read deck:write tables:read tables:write files:read files:write sharing:read sharing:write",
    show_default=True,
    help="OAuth scopes to request during client registration. These define the maximum allowed scopes for the client. Note: Actual supported scopes are discovered dynamically from MCP tools at runtime. (can also use NEXTCLOUD_OIDC_SCOPES env var)",
)
@click.option(
    "--oauth-token-type",
    envvar="NEXTCLOUD_OIDC_TOKEN_TYPE",
    default="bearer",
    show_default=True,
    type=click.Choice(["bearer", "jwt"], case_sensitive=False),
    help="OAuth token type (can also use NEXTCLOUD_OIDC_TOKEN_TYPE env var)",
)
@click.option(
    "--public-issuer-url",
    envvar="NEXTCLOUD_PUBLIC_ISSUER_URL",
    help="Public issuer URL for OAuth (can also use NEXTCLOUD_PUBLIC_ISSUER_URL env var)",
)
def run(
    host: str,
    port: int,
    log_level: str,
    transport: str,
    enable_app: tuple[str, ...],
    oauth: bool | None,
    oauth_client_id: str | None,
    oauth_client_secret: str | None,
    mcp_server_url: str,
    nextcloud_host: str | None,
    nextcloud_username: str | None,
    nextcloud_password: str | None,
    oauth_scopes: str,
    oauth_token_type: str,
    public_issuer_url: str | None,
):
    """
    Run the Nextcloud MCP server.

    \b
    Authentication Modes:
      - BasicAuth: Set NEXTCLOUD_USERNAME and NEXTCLOUD_PASSWORD
      - OAuth: Leave USERNAME/PASSWORD unset (requires OIDC app enabled)

    \b
    Examples:
      # BasicAuth mode with CLI options
      $ nextcloud-mcp-server --nextcloud-host=https://cloud.example.com \\
          --nextcloud-username=admin --nextcloud-password=secret

      # BasicAuth mode with env vars (recommended for credentials)
      $ export NEXTCLOUD_HOST=https://cloud.example.com
      $ export NEXTCLOUD_USERNAME=admin
      $ export NEXTCLOUD_PASSWORD=secret
      $ nextcloud-mcp-server --host 0.0.0.0 --port 8000

      # OAuth mode with auto-registration
      $ nextcloud-mcp-server --nextcloud-host=https://cloud.example.com --oauth

      # OAuth mode with pre-configured client
      $ nextcloud-mcp-server --nextcloud-host=https://cloud.example.com --oauth \\
          --oauth-client-id=xxx --oauth-client-secret=yyy

      # OAuth mode with custom scopes and JWT tokens
      $ nextcloud-mcp-server --nextcloud-host=https://cloud.example.com --oauth \\
          --oauth-scopes="openid notes:read notes:write" --oauth-token-type=jwt

      # OAuth with public issuer URL (for Docker/proxy setups)
      $ nextcloud-mcp-server --nextcloud-host=http://app --oauth \\
          --public-issuer-url=http://localhost:8080
    """
    # Set env vars from CLI options if provided
    if nextcloud_host:
        os.environ["NEXTCLOUD_HOST"] = nextcloud_host
    if nextcloud_username:
        os.environ["NEXTCLOUD_USERNAME"] = nextcloud_username
    if nextcloud_password:
        os.environ["NEXTCLOUD_PASSWORD"] = nextcloud_password
    if oauth_client_id:
        os.environ["NEXTCLOUD_OIDC_CLIENT_ID"] = oauth_client_id
    if oauth_client_secret:
        os.environ["NEXTCLOUD_OIDC_CLIENT_SECRET"] = oauth_client_secret
    if oauth_scopes:
        os.environ["NEXTCLOUD_OIDC_SCOPES"] = oauth_scopes
    if oauth_token_type:
        os.environ["NEXTCLOUD_OIDC_TOKEN_TYPE"] = oauth_token_type
    if mcp_server_url:
        os.environ["NEXTCLOUD_MCP_SERVER_URL"] = mcp_server_url
    if public_issuer_url:
        os.environ["NEXTCLOUD_PUBLIC_ISSUER_URL"] = public_issuer_url

    # Force OAuth mode if explicitly requested
    if oauth is True:
        # Clear username/password to force OAuth mode
        if "NEXTCLOUD_USERNAME" in os.environ:
            click.echo(
                "Warning: --oauth flag set, ignoring NEXTCLOUD_USERNAME", err=True
            )
            del os.environ["NEXTCLOUD_USERNAME"]
        if "NEXTCLOUD_PASSWORD" in os.environ:
            click.echo(
                "Warning: --oauth flag set, ignoring NEXTCLOUD_PASSWORD", err=True
            )
            del os.environ["NEXTCLOUD_PASSWORD"]

        # Validate OAuth configuration
        nextcloud_host = os.getenv("NEXTCLOUD_HOST")
        if not nextcloud_host:
            raise click.ClickException(
                "OAuth mode requires NEXTCLOUD_HOST environment variable to be set"
            )

        # Check if we have client credentials OR if dynamic registration is possible
        has_client_creds = os.getenv("NEXTCLOUD_OIDC_CLIENT_ID") and os.getenv(
            "NEXTCLOUD_OIDC_CLIENT_SECRET"
        )

        if not has_client_creds:
            # No client credentials - will attempt dynamic registration
            # Show helpful message before server starts
            click.echo("", err=True)
            click.echo("OAuth Configuration:", err=True)
            click.echo("  Mode: Dynamic Client Registration", err=True)
            click.echo("  Host: " + nextcloud_host, err=True)
            click.echo("  Storage: SQLite (TOKEN_STORAGE_DB)", err=True)
            click.echo("", err=True)
            click.echo(
                "Note: Make sure 'Dynamic Client Registration' is enabled", err=True
            )
            click.echo("      in your Nextcloud OIDC app settings.", err=True)
            click.echo("", err=True)
        else:
            click.echo("", err=True)
            click.echo("OAuth Configuration:", err=True)
            click.echo("  Mode: Pre-configured Client", err=True)
            click.echo("  Host: " + nextcloud_host, err=True)
            click.echo(
                "  Client ID: "
                + os.getenv("NEXTCLOUD_OIDC_CLIENT_ID", "")[:16]
                + "...",
                err=True,
            )
            click.echo("", err=True)

    elif oauth is False:
        # Force BasicAuth mode - verify credentials exist
        if not os.getenv("NEXTCLOUD_USERNAME") or not os.getenv("NEXTCLOUD_PASSWORD"):
            raise click.ClickException(
                "--no-oauth flag set but NEXTCLOUD_USERNAME or NEXTCLOUD_PASSWORD not set"
            )

    enabled_apps = list(enable_app) if enable_app else None

    app = get_app(transport=transport, enabled_apps=enabled_apps)

    # Get observability settings and create uvicorn logging config
    settings = get_settings()
    uvicorn_log_config = get_uvicorn_logging_config(
        log_format=settings.log_format,
        log_level=settings.log_level,
        include_trace_context=settings.log_include_trace_context,
    )

    uvicorn.run(
        app=app,
        host=host,
        port=port,
        log_level=log_level,
        log_config=uvicorn_log_config,
    )


if __name__ == "__main__":
    run()
