from __future__ import annotations

import logging

from cognite.client import CogniteClient
from cognite.client.data_classes.data_modeling import ViewId
from mcp.server.fastmcp import FastMCP

from cog_mcp.config import Config
from cog_mcp.formatting import (
    format_data_models_overview,
    format_view_schema,
)

logger = logging.getLogger(__name__)


def fetch_views(client: CogniteClient, config: Config) -> str:
    """Fetch all configured views and return formatted overview."""
    all_models = []
    for dm_id in config.data_models:
        try:
            result = client.data_modeling.data_models.retrieve(
                ids=[dm_id], inline_views=True
            )
            if result:
                all_models.extend(result)
        except Exception as e:
            logger.error(f"Failed to retrieve data model {dm_id}: {e}")

    return format_data_models_overview(all_models)


def fetch_view_schema(client: CogniteClient, space: str, external_id: str, version: str) -> str:
    """Fetch and return formatted schema for a single view."""
    view_id = ViewId(space=space, external_id=external_id, version=version)
    result = client.data_modeling.views.retrieve(
        ids=[view_id], include_inherited_properties=True
    )
    if not result:
        return f'{{"error": "View {space}/{external_id}/{version} not found"}}'

    return format_view_schema(result[0])


def register_resources(mcp: FastMCP, client: CogniteClient, config: Config) -> None:
    """Register all MCP resources on the FastMCP server."""

    @mcp.resource("cdf://views")
    def list_views() -> str:
        """List all available views with their space, externalId, and version.

        Use these values when calling tools like list_instances or search_instances.
        To get full property schemas, read cdf://views/{space}/{externalId}/{version}.
        """
        return fetch_views(client, config)

    @mcp.resource("cdf://views/{space}/{external_id}/{version}")
    def get_view_schema(space: str, external_id: str, version: str) -> str:
        """Get the full schema of a view including all property names, types, and relations.

        Read this before filtering, sorting, or aggregating to get correct property names.
        Get the space, externalId, and version from cdf://views.
        """
        return fetch_view_schema(client, space, external_id, version)

    @mcp.resource("cdf://docs/filters")
    def filter_docs() -> str:
        """Reference documentation for CDF instance filter syntax.

        Read this resource when you need to construct filter expressions for
        list_instances, search_instances, or aggregate_instances tools.
        """
        return FILTER_DOCS

    @mcp.resource("cdf://docs/querying")
    def query_docs() -> str:
        """Reference documentation for the CDF graph query API.

        Read this resource when you need to construct queries for the
        query_instances tool, including traversals and multi-result-set queries.
        """
        return QUERY_DOCS


FILTER_DOCS = """\
# CDF Instance Filter Syntax

Filters are JSON objects passed to `list_instances`, `search_instances`, and `aggregate_instances`.

## Property references

View properties use a 3-element array: `[viewSpace, viewExternalId/viewVersion, propertyName]`.

Node built-in properties use: `["node", propertyName]`
Available: space, externalId, version, type, createdTime, lastUpdatedTime, deletedTime

Edge built-in properties use: `["edge", propertyName]`
Available: space, externalId, version, type, startNode, endNode, createdTime, lastUpdatedTime, deletedTime

## Basic Filters

### Equals
```json
{
  "equals": {
    "property": ["mySpace", "MyView/v1", "status"],
    "value": "Active"
  }
}
```

### In (match any of several values)
```json
{
  "in": {
    "property": ["mySpace", "MyView/v1", "status"],
    "values": ["Active", "Planned"]
  }
}
```

### Range (gt, gte, lt, lte)
```json
{
  "range": {
    "property": ["mySpace", "MyView/v1", "priority"],
    "gte": 1,
    "lte": 5
  }
}
```

### Prefix
```json
{
  "prefix": {
    "property": ["mySpace", "MyView/v1", "name"],
    "value": "Pump"
  }
}
```

### Exists (property is not null)
```json
{
  "exists": {
    "property": ["mySpace", "MyView/v1", "endTime"]
  }
}
```

### Direct Relation (filtering on a reference/relation property)

Direct relation properties point to other nodes. Use a dict with "space" and
"externalId" as the value:
```json
{
  "equals": {
    "property": ["mySpace", "MyView/v1", "asset"],
    "value": {"space": "targetSpace", "externalId": "targetExternalId"}
  }
}
```

To match any of several related nodes:
```json
{
  "in": {
    "property": ["mySpace", "MyView/v1", "asset"],
    "values": [
      {"space": "s1", "externalId": "asset1"},
      {"space": "s1", "externalId": "asset2"}
    ]
  }
}
```

### ContainsAny / ContainsAll (for list properties)
```json
{
  "containsAny": {
    "property": ["mySpace", "MyView/v1", "tags"],
    "values": ["critical", "safety"]
  }
}
```

## Combining Filters

### And
```json
{
  "and": [
    {"equals": {"property": ["mySpace", "MyView/v1", "status"], "value": "Active"}},
    {"range": {"property": ["mySpace", "MyView/v1", "priority"], "gte": 3}}
  ]
}
```

### Or
```json
{
  "or": [
    {"equals": {"property": ["mySpace", "MyView/v1", "status"], "value": "Active"}},
    {"equals": {"property": ["mySpace", "MyView/v1", "status"], "value": "Planned"}}
  ]
}
```

### Not
```json
{
  "not": {
    "equals": {"property": ["mySpace", "MyView/v1", "status"], "value": "Cancelled"}
  }
}
```

## Nested Filters (filtering on related instances)

Use `hasData` to filter on instances that have data in a specific view:
```json
{
  "hasData": [{"space": "mySpace", "externalId": "MyView", "version": "v1", "type": "view"}]
}
```

## Notes

- String comparisons are case-sensitive
- Timestamps use ISO 8601 format: `"2024-01-15T00:00:00Z"`
- `null` values can be checked with `exists` / `not exists`
- Instance space filtering is automatically applied by the server
"""

QUERY_DOCS = """\
# CDF Graph Query Syntax

The `query_instances` tool executes graph queries that can traverse relationships
and return data from multiple views in a single call.

## Query Structure

```json
{
  "with": {
    "<result_set_name>": {
      "nodes": { ... }    // or "edges": { ... }
    }
  },
  "select": {
    "<result_set_name>": {
      "sources": [
        {
          "source": {"space": "s", "externalId": "ViewId", "version": "v1"},
          "properties": ["prop1", "prop2"]
        }
      ]
    }
  }
}
```

## Basic Query: List Nodes from a View

```json
{
  "with": {
    "orders": {
      "nodes": {
        "filter": {
          "hasData": [{"space": "cdf_idm", "externalId": "CogniteMaintenanceOrder", "version": "v1", "type": "view"}]
        },
        "limit": 10
      }
    }
  },
  "select": {
    "orders": {
      "sources": [
        {
          "source": {"space": "cdf_idm", "externalId": "CogniteMaintenanceOrder", "version": "v1"},
          "properties": ["name", "description", "status", "priority", "startTime", "endTime"]
        }
      ]
    }
  }
}
```

## Traversal Query: Follow a Relationship

Start from one set of nodes and traverse a relationship to related nodes.

```json
{
  "with": {
    "assets": {
      "nodes": {
        "filter": {
          "equals": {
            "property": ["cdf_idm", "CogniteAsset/v1", "name"],
            "value": "Pump-001"
          }
        }
      }
    },
    "related_orders": {
      "nodes": {
        "from": "assets",
        "through": {"source": {"space": "cdf_idm", "externalId": "CogniteMaintenanceOrder", "version": "v1"}, "identifier": "asset"},
        "direction": "inwards"
      }
    }
  },
  "select": {
    "assets": {
      "sources": [
        {
          "source": {"space": "cdf_idm", "externalId": "CogniteAsset", "version": "v1"},
          "properties": ["name", "description"]
        }
      ]
    },
    "related_orders": {
      "sources": [
        {
          "source": {"space": "cdf_idm", "externalId": "CogniteMaintenanceOrder", "version": "v1"},
          "properties": ["name", "status", "priority"]
        }
      ]
    }
  }
}
```

## Key Concepts

### Result Set Expressions (`with`)
- `nodes`: Match node instances
- `edges`: Match edge instances
- `from`: Chain from another result set (for traversals)
- `through`: The relationship property to traverse (reference: `{"source": {"space": "...", "externalId": "...", "version": "..."}, "identifier": "propertyName"}`)
- `direction`: `"outwards"` (default) or `"inwards"`
- `filter`: Optional filter (same syntax as list_instances filters)
- `limit`: Max results per result set

### Select
- Maps result set names to the properties you want returned
- Each source specifies a view and which properties to include
- Omit `properties` to get all properties from that view

### Pagination
- Pass `"cursors": {"<result_set>": "<cursor_value>"}` from a previous response to page through results

## Notes

- Instance space filters are automatically injected by the server
- Each result set can have its own limit
- Traversals always start from a root result set (one without `from`)
- Multiple traversal hops are supported by chaining result sets
"""
