"""FastMCP server — tools, resources, and prompts for the ntro platform."""

from __future__ import annotations

import json

from mcp.server.fastmcp import FastMCP

from ntro.workspace import Client


def create_server(connection: str | None = None) -> FastMCP:
    mcp = FastMCP(
        "ntro",
        instructions="""You are connected to the ntro platform for fund operations automation.
Use the available tools to manage tenants, entities, workflows, and task runs.
Use resources to load current platform state before taking actions.

Key concepts:
- Tenant = a client organisation (fund admin or asset manager)
- Entity = an SPV or fund within a tenant
- Workflow = a repeatable process (NAV calc, document ingestion, period close)
- Task = a running instance of a workflow
- Integration = a data platform connection (Databricks, Snowflake)
""",
    )

    # Lazy client — created once on first use
    _client: Client | None = None

    def get_client() -> Client:
        nonlocal _client
        if _client is None:
            _client = Client.from_config(connection=connection)
        return _client

    def _dump(obj) -> dict:
        return obj.model_dump(mode="json") if hasattr(obj, "model_dump") else dict(obj)

    # ══════════════════════════════════════════════════════════════
    #  TOOLS — write / action operations
    # ══════════════════════════════════════════════════════════════

    @mcp.tool()
    async def ntro_whoami() -> dict:
        """Get the current authenticated user identity and workspace info."""
        c = get_client()
        user = await c.identity.whoami()
        return _dump(user)

    @mcp.tool()
    async def ntro_integration_create(
        name: str,
        provider: str,
        region: str,
        config: dict,
        ownership: str = "SELF_MANAGED",
    ) -> dict:
        """Register a new data platform integration (Databricks, Snowflake, etc).

        Args:
            name: Display name (e.g., "Acme Databricks UK")
            provider: DATABRICKS, SNOWFLAKE, MICROSOFT_FABRIC, or GCP_BIGQUERY
            region: Deployment region (e.g., "UK-South")
            config: Provider-specific config. For Databricks: {workspaceUrl, catalog, authType, clientId, clientSecret}
            ownership: SELF_MANAGED (customer owns) or NTRO_MANAGED
        """
        c = get_client()
        result = await c.integrations.create_data_platform(
            name=name, provider=provider, region=region,
            config=config, ownership=ownership,
        )
        return _dump(result)

    @mcp.tool()
    async def ntro_integration_test(id: str) -> dict:
        """Test connectivity to a registered data platform. Returns success/fail with diagnostics.

        Args:
            id: Data platform config ID
        """
        c = get_client()
        result = await c.integrations.test_connection(id)
        return _dump(result)

    @mcp.tool()
    async def ntro_tenant_create(
        name: str,
        slug: str,
        data_platform_config_id: str | None = None,
    ) -> dict:
        """Create a new tenant (client cell). A tenant is a fund admin or asset manager organisation.

        Args:
            name: Display name (e.g., "Acme Fund Administration")
            slug: URL-safe identifier (e.g., "acme-fund-admin")
            data_platform_config_id: ID of the data platform config to link (optional)
        """
        c = get_client()
        tenant = await c.tenants.create(
            name=name, slug=slug, dataPlatformConfigId=data_platform_config_id,
        )
        return _dump(tenant)

    @mcp.tool()
    async def ntro_entity_create(
        tenant_id: str,
        name: str,
        slug: str,
        type: str | None = None,
        jurisdiction: str | None = None,
        currency: str | None = None,
        schema: str | None = None,
    ) -> dict:
        """Create an entity (SPV/fund) within a tenant. Each entity gets its own schema in the data platform.

        Args:
            tenant_id: Tenant slug or ID
            name: Display name (e.g., "Acme Commercial SPV 1")
            slug: URL-safe identifier (e.g., "acme-commercial-spv1")
            type: Entity type (e.g., "real-estate-spv", "private-credit-fund")
            jurisdiction: Legal jurisdiction (e.g., "Jersey", "Luxembourg", "ADGM")
            currency: Base currency (e.g., "GBP", "EUR", "USD")
            schema: Target schema name in the data platform (e.g., "nav_pipeline_spv1")
        """
        c = get_client()
        entity = await c.entities.create(
            tenant_id=tenant_id, name=name, slug=slug,
            type=type, jurisdiction=jurisdiction,
            currency=currency, schema_=schema,
        )
        return _dump(entity)

    @mcp.tool()
    async def ntro_workflow_create(
        name: str,
        description: str = "",
        tenant_id: str | None = None,
        schedule: str | None = None,
        timezone: str = "Europe/London",
    ) -> dict:
        """Register a new workflow definition.

        Args:
            name: Workflow name/slug (e.g., "nav-monthly", "coa-import")
            description: Human-readable description
            tenant_id: Tenant slug or ID this workflow belongs to
            schedule: Cron expression (e.g., "0 8 5 * *" = 8am on 5th of each month). None = manual only.
            timezone: Timezone for the schedule (default: Europe/London)
        """
        c = get_client()
        workflow = await c.workflows.create(
            name=name, description=description, tenantId=tenant_id,
            schedule=schedule, timezone=timezone,
        )
        return _dump(workflow)

    @mcp.tool()
    async def ntro_workflow_deploy(
        workflow_id: str,
        workflow_version_id: str,
        tenant_id: str | None = None,
        entity_id: str | None = None,
    ) -> dict:
        """Deploy a workflow version to a tenant cell.

        Args:
            workflow_id: Workflow ID to deploy
            workflow_version_id: Version ID to deploy
            tenant_id: Target tenant ID (optional)
            entity_id: Target entity ID (optional)
        """
        c = get_client()
        deployment = await c.deployments.create(
            workflowId=workflow_id,
            workflowVersionId=workflow_version_id,
            tenantId=tenant_id,
            entityId=entity_id,
        )
        return _dump(deployment)

    @mcp.tool()
    async def ntro_workflow_run(
        tenant_id: str,
        workflow_id: str,
        entity_id: str | None = None,
        period: str | None = None,
        priority: str | None = None,
        dry_run: bool = False,
    ) -> dict:
        """Trigger a workflow run. Returns a task ID you can use to track progress.

        Args:
            tenant_id: Tenant slug or ID
            workflow_id: Workflow name or ID (e.g., "nav-monthly", "coa-import", "document-ingest")
            entity_id: Entity slug or ID (optional — required for entity-scoped workflows)
            period: Accounting period (e.g., "2026-03")
            priority: LOW, NORMAL, or HIGH
            dry_run: If true, validate without committing changes
        """
        context: dict = {}
        if period:
            context["period"] = period
        if priority:
            context["priority"] = priority
        if dry_run:
            context["dry_run"] = True

        c = get_client()
        task = await c.tasks.create(
            tenantId=tenant_id, entityId=entity_id,
            workflowId=workflow_id, context=context,
        )
        return _dump(task)

    # ══════════════════════════════════════════════════════════════
    #  RESOURCES — read-only context for Claude
    # ══════════════════════════════════════════════════════════════

    @mcp.resource("ntro://whoami")
    async def resource_whoami() -> str:
        """Current authenticated user and workspace info."""
        c = get_client()
        user = await c.identity.whoami()
        return json.dumps(_dump(user), indent=2)

    @mcp.resource("ntro://integrations")
    async def resource_integrations() -> str:
        """All configured data platform integrations."""
        c = get_client()
        platforms = await c.integrations.list_data_platforms()
        return json.dumps([_dump(p) for p in platforms], indent=2)

    @mcp.resource("ntro://integrations/{id}")
    async def resource_integration(id: str) -> str:
        """Detail for a specific data platform integration."""
        c = get_client()
        platform = await c.integrations.get_data_platform(id)
        return json.dumps(_dump(platform), indent=2)

    @mcp.resource("ntro://integrations/{id}/schemas")
    async def resource_schemas(id: str) -> str:
        """Discover schemas and tables in a customer's data platform."""
        c = get_client()
        schemas = await c.integrations.discover_schemas(id)
        return json.dumps([_dump(s) for s in schemas], indent=2)

    @mcp.resource("ntro://tenants")
    async def resource_tenants() -> str:
        """All tenants with status and entity counts."""
        c = get_client()
        tenants = await c.tenants.list()
        return json.dumps([_dump(t) for t in tenants], indent=2)

    @mcp.resource("ntro://tenants/{slug}")
    async def resource_tenant(slug: str) -> str:
        """Detail for a specific tenant."""
        c = get_client()
        tenant = await c.tenants.get(slug)
        return json.dumps(_dump(tenant), indent=2)

    @mcp.resource("ntro://entities")
    async def resource_entities() -> str:
        """All entities (SPVs/funds) across all tenants."""
        c = get_client()
        entities = await c.entities.list()
        return json.dumps([_dump(e) for e in entities], indent=2)

    @mcp.resource("ntro://workflows")
    async def resource_workflows() -> str:
        """All registered workflow definitions."""
        c = get_client()
        workflows = await c.workflows.list()
        return json.dumps([_dump(w) for w in workflows], indent=2)

    @mcp.resource("ntro://workflows/{id}")
    async def resource_workflow(id: str) -> str:
        """Workflow definition detail."""
        c = get_client()
        workflow = await c.workflows.get(id)
        return json.dumps(_dump(workflow), indent=2)

    @mcp.resource("ntro://tasks/{id}")
    async def resource_task(id: str) -> str:
        """Task (workflow run) status including step progress."""
        c = get_client()
        task = await c.tasks.get(id)
        return json.dumps(_dump(task), indent=2)

    @mcp.resource("ntro://schedule")
    async def resource_schedule() -> str:
        """All scheduled and active tasks sorted by urgency."""
        c = get_client()
        tasks = await c.tasks.list_schedule()
        return json.dumps([_dump(t) for t in tasks], indent=2)

    # ══════════════════════════════════════════════════════════════
    #  PROMPTS — guided multi-step workflows
    # ══════════════════════════════════════════════════════════════

    @mcp.prompt()
    def onboard_spv(
        tenant_slug: str,
        spv_name: str,
        jurisdiction: str = "Jersey",
        currency: str = "GBP",
    ) -> str:
        """Guide through setting up a new SPV entity end-to-end."""
        return f"""You are onboarding a new SPV onto the ntro platform.

Tenant: {tenant_slug}
SPV Name: {spv_name}
Jurisdiction: {jurisdiction}
Currency: {currency}

Follow these steps in order:
1. Load ntro://tenants/{tenant_slug} to verify the tenant exists and get its data platform config
2. Create the entity using ntro_entity_create with an appropriate schema name (snake_case slug)
3. Ask the user for their Chart of Accounts (CSV file or details)
4. Trigger the coa-import workflow using ntro_workflow_run
5. Ask if they want to configure email integrations for document collection
6. Register the nav-monthly workflow using ntro_workflow_create with a monthly cron schedule
7. Offer to run a dry-run test with ntro_workflow_run (dry_run=true)

Confirm success at each step before proceeding. If any step fails, explain the error clearly."""

    @mcp.prompt()
    def run_nav(
        tenant_slug: str,
        entity_slug: str,
        period: str,
    ) -> str:
        """Guide through executing a monthly NAV calculation for a specific entity and period."""
        return f"""Execute the monthly NAV workflow:

Tenant: {tenant_slug}
Entity: {entity_slug}
Period: {period}

Steps:
1. Load ntro://tenants/{tenant_slug} to verify the entity exists and is active
2. Trigger nav-monthly using ntro_workflow_run with tenant_id, entity_id, and period="{period}"
3. Store the returned task ID and monitor progress via ntro://tasks/{{task_id}}
4. When steps show BLOCKED status, inform the user what needs human review/approval
5. Once complete, summarise the result including any errors or warnings"""

    @mcp.prompt()
    def platform_status() -> str:
        """Get a comprehensive overview of the current platform state."""
        return """Provide a full status overview of the ntro platform:

1. Load ntro://whoami to confirm connection and identity
2. Load ntro://integrations to list data platform connections
3. Load ntro://tenants to list all client cells
4. Load ntro://entities to list all SPVs and funds
5. Load ntro://workflows to list registered workflow definitions
6. Load ntro://schedule to check scheduled, active, and pending tasks

Summarise clearly:
- How many tenants, entities, workflows, and integrations are configured
- Any active or recently completed tasks
- Anything that needs attention (failed runs, BLOCKED tasks, missing integrations)
- Overall platform health"""

    return mcp
