"""CRUD de cards/preguntas de Metabase y ejecucion de queries."""

from .client import MetabaseClient


def metabase_list_cards(
    client: MetabaseClient,
    filter: str = "",
    model_id: int = 0,
) -> list[dict]:
    """Lista preguntas/cards de Metabase con filtro opcional.

    Endpoint: GET /api/card. No tiene paginacion offset/limit.

    Args:
        client: Cliente autenticado.
        filter: "all", "mine", "fav", "archived", "recent", "popular",
                "database", "table". Vacio = todas.
        model_id: ID de database/tabla. Solo aplica con filter "database" o "table".

    Returns:
        Lista de dicts, cada uno con: id, name, description, display,
        collection_id, database_id, creator_id, archived, dataset_query.

    Example:
        >>> cards = metabase_list_cards(client, filter="mine")
        >>> for c in cards:
        ...     print(c["id"], c["name"], c["display"])
    """
    params = {}
    if filter:
        params["f"] = filter
    if model_id > 0:
        params["model_id"] = model_id
    return client.request("GET", "/api/card", params=params)


def metabase_get_card(client: MetabaseClient, card_id: int) -> dict:
    """Obtiene los detalles completos de una card/pregunta.

    Endpoint: GET /api/card/:id.

    Args:
        client: Cliente autenticado.
        card_id: ID de la card.

    Returns:
        Dict con: id, name, description, display, dataset_query,
        visualization_settings, collection_id, database_id, archived,
        creator, created_at, updated_at.

    Example:
        >>> card = metabase_get_card(client, 42)
        >>> print(card["name"], card["display"])
        >>> print(card["dataset_query"]["native"]["query"])  # SQL
    """
    return client.request("GET", f"/api/card/{card_id}")


def metabase_create_card(
    client: MetabaseClient,
    name: str,
    dataset_query: dict,
    display: str = "table",
    collection_id: int = 0,
    description: str = "",
) -> dict:
    """Crea una nueva card/pregunta en Metabase.

    Endpoint: POST /api/card.

    Args:
        client: Cliente autenticado.
        name: Nombre de la pregunta.
        dataset_query: Query de la card. Estructura:
            SQL nativo: {"database": 1, "type": "native", "native": {"query": "SELECT ..."}}
            MBQL: {"database": 1, "type": "query", "query": {"source-table": 4, ...}}
        display: Tipo de visualizacion: "table", "bar", "line", "pie", "scalar",
                 "area", "row", "combo", "funnel", "scatter", "waterfall", etc.
        collection_id: ID de coleccion destino. 0 = root.
        description: Descripcion opcional.

    Returns:
        Dict con la card creada.

    Example:
        >>> card = metabase_create_card(client, "Revenue by Month", {
        ...     "database": 1,
        ...     "type": "native",
        ...     "native": {"query": "SELECT date_trunc('month', created_at), SUM(total) FROM orders GROUP BY 1"},
        ... }, display="line", description="Monthly revenue trend")
    """
    body: dict = {
        "name": name,
        "dataset_query": dataset_query,
        "display": display,
        "visualization_settings": {},
    }
    if collection_id > 0:
        body["collection_id"] = collection_id
    if description:
        body["description"] = description
    return client.request("POST", "/api/card", json=body)


def metabase_update_card(client: MetabaseClient, card_id: int, **fields) -> dict:
    """Actualiza campos de una card/pregunta en Metabase.

    Endpoint: PUT /api/card/:id. Solo se modifican los campos pasados.

    Args:
        client: Cliente autenticado.
        card_id: ID de la card.
        **fields: Campos a actualizar. Validos:
            name (str), description (str), display (str),
            dataset_query (dict), visualization_settings (dict),
            collection_id (int), archived (bool),
            enable_embedding (bool), embedding_params (dict).

    Returns:
        Dict con la card actualizada.

    Example:
        >>> metabase_update_card(client, 42, name="Updated Name", archived=True)
        >>> metabase_update_card(client, 42, dataset_query={
        ...     "database": 1, "type": "native",
        ...     "native": {"query": "SELECT * FROM users LIMIT 100"},
        ... })
    """
    return client.request("PUT", f"/api/card/{card_id}", json=fields)


def metabase_delete_card(client: MetabaseClient, card_id: int) -> None:
    """Elimina permanentemente una card/pregunta.

    Endpoint: DELETE /api/card/:id. IRREVERSIBLE.
    Para soft-delete preferir: metabase_update_card(client, card_id, archived=True)

    Args:
        client: Cliente autenticado.
        card_id: ID de la card a eliminar.

    Example:
        >>> metabase_delete_card(client, 42)
        >>> # Preferir soft-delete: metabase_update_card(client, 42, archived=True)
    """
    client.request("DELETE", f"/api/card/{card_id}")


def metabase_execute_card(
    client: MetabaseClient,
    card_id: int,
    parameters: list[dict] | None = None,
) -> dict:
    """Ejecuta la query de una card/pregunta guardada.

    Endpoint: POST /api/card/:id/query.

    Args:
        client: Cliente autenticado.
        card_id: ID de la card a ejecutar.
        parameters: Parametros para queries parametrizadas. Cada parametro:
            {"type": "category", "target": ["variable", ["template-tag", "tag"]], "value": "val"}

    Returns:
        Dict con resultados:
        - status: "completed" o "failed"
        - row_count: numero de filas
        - running_time: tiempo en ms
        - data.columns: nombres de columnas
        - data.rows: filas de datos (lista de listas)
        - data.cols: metadata de columnas
        - data.native_form.query: SQL ejecutado

    Example:
        >>> result = metabase_execute_card(client, 42)
        >>> for row in result["data"]["rows"]:
        ...     print(row)
        >>> # Con parametros:
        >>> result = metabase_execute_card(client, 42, parameters=[
        ...     {"type": "category", "target": ["variable", ["template-tag", "status"]], "value": "active"},
        ... ])
    """
    body = {}
    if parameters:
        body["parameters"] = parameters
    return client.request("POST", f"/api/card/{card_id}/query", json=body or None)


def metabase_execute_query(
    client: MetabaseClient,
    database_id: int,
    sql: str,
    max_results: int = 0,
) -> dict:
    """Ejecuta una query SQL ad-hoc sin guardarla como card.

    Endpoint: POST /api/dataset. Util para exploracion rapida y consultas
    que no necesitan persistirse.

    Args:
        client: Cliente autenticado.
        database_id: ID de la database en Metabase.
        sql: Query SQL a ejecutar.
        max_results: Limite de filas. 0 = default 2000.

    Returns:
        Dict con misma estructura que metabase_execute_card:
        data.columns, data.rows, row_count, running_time, status.

    Example:
        >>> result = metabase_execute_query(client, 1, "SELECT * FROM users LIMIT 10")
        >>> print(f"{result['row_count']} filas en {result['running_time']}ms")
        >>> for row in result["data"]["rows"]:
        ...     print(row)
    """
    body: dict = {
        "database": database_id,
        "type": "native",
        "native": {"query": sql},
    }
    if max_results > 0:
        body["constraints"] = {
            "max-results": max_results,
            "max-results-bare-rows": max_results,
        }
    return client.request("POST", "/api/dataset", json=body)


def metabase_copy_card(
    client: MetabaseClient,
    card_id: int,
    name: str | None = None,
    collection_id: int | None = None,
    description: str | None = None,
) -> dict:
    """Crea una copia de una card/pregunta existente en Metabase.

    Endpoint: POST /api/card/:id/copy. Usa el endpoint nativo de Metabase para
    duplicar la card, copiando dataset_query, display y visualization_settings.
    Los campos name, collection_id y description se pueden sobrescribir via body.

    Args:
        client: Cliente autenticado.
        card_id: ID de la card a copiar.
        name: Nombre para la copia. None = Metabase asigna "Copy of <nombre>".
        collection_id: Coleccion destino. None = misma coleccion que el original.
        description: Descripcion de la copia. None = misma que el original.

    Returns:
        Dict con la card nueva creada por Metabase. Incluye el campo `id`
        asignado a la copia y todos los campos heredados del original.

    Example:
        >>> copy = metabase_copy_card(client, 42)
        >>> print(copy["id"], copy["name"])  # "Copy of ..."
        >>> # Copiar a otra coleccion con nombre propio:
        >>> copy = metabase_copy_card(client, 42, name="Revenue Q2", collection_id=7)
    """
    body: dict = {}
    if name is not None:
        body["name"] = name
    if collection_id is not None:
        body["collection_id"] = collection_id
    if description is not None:
        body["description"] = description
    return client.request("POST", f"/api/card/{card_id}/copy", json=body or None)


def metabase_move_card(
    client: MetabaseClient,
    card_id: int,
    collection_id: int | None,
) -> dict:
    """Mueve una card/pregunta a otra coleccion.

    Wrapper thin sobre PUT /api/card/:id que solo actualiza collection_id.
    Equivalente a metabase_update_card(client, card_id, collection_id=...) pero
    con intencion explicita y soporte para mover a root con None.

    Endpoint: PUT /api/card/:id.

    Args:
        client: Cliente autenticado.
        card_id: ID de la card a mover.
        collection_id: ID de la coleccion destino. None mueve a "Our analytics" (root).

    Returns:
        Dict con la card actualizada, incluyendo el nuevo collection_id.

    Example:
        >>> card = metabase_move_card(client, 42, collection_id=7)
        >>> print(card["collection_id"])  # 7
        >>> # Mover a root:
        >>> card = metabase_move_card(client, 42, collection_id=None)
    """
    return client.request("PUT", f"/api/card/{card_id}", json={"collection_id": collection_id})


def metabase_create_card_raw(client: MetabaseClient, payload: dict) -> dict:
    """Crea una card en Metabase con payload completo ya construido por el caller.

    Version raw de metabase_create_card. El caller es responsable de construir
    el payload completo antes de llamar a esta funcion — no se realiza ninguna
    validacion ni transformacion local. Util para flujos "Metabase as code"
    donde el YAML define todos los campos de la card tal como los espera la API.

    Endpoint: POST /api/card.

    El payload minimo necesita:
        - name (str): nombre de la card.
        - dataset_query (dict): query SQL nativa o MBQL.
        - display (str): tipo de visualizacion (table, bar, scalar, etc.).

    Campos opcionales que esta funcion preserva (a diferencia de metabase_create_card):
        - visualization_settings (dict): configuracion detallada del grafico.
        - parameters (list[dict]): parametros de la query con template tags.
        - parameter_mappings (list[dict]): mapeo de parametros a dashboard filters.
        - type (str): "question" (default), "model", "metric".
        - collection_id (int): ID de coleccion destino.
        - description (str): descripcion de la card.
        - archived (bool): estado de archivo inicial.
        - enable_embedding (bool): habilitar embedding publico.
        - embedding_params (dict): configuracion de embedding.

    Si Metabase devuelve 4xx/5xx, httpx lanza HTTPStatusError sin capturar.

    Args:
        client: Cliente autenticado con sesion activa.
        payload: Dict con el payload completo de la card tal como lo espera
                 la API de Metabase. Se envia sin modificaciones.

    Returns:
        Dict con la card recien creada. Incluye el campo `id` asignado por
        Metabase y todos los campos normalizados (display, dataset_query,
        visualization_settings, created_at, etc.).

    Example:
        >>> card = metabase_create_card_raw(client, {
        ...     "name": "Revenue by Month",
        ...     "dataset_query": {
        ...         "database": 1,
        ...         "type": "native",
        ...         "native": {"query": "SELECT date_trunc('month', created_at), SUM(total) FROM orders GROUP BY 1"},
        ...     },
        ...     "display": "line",
        ...     "visualization_settings": {
        ...         "graph.x_axis.title_text": "Month",
        ...         "graph.y_axis.title_text": "Revenue",
        ...     },
        ...     "description": "Monthly revenue trend",
        ...     "collection_id": 5,
        ... })
        >>> print(card["id"])  # ID asignado por Metabase
    """
    return client.request("POST", "/api/card", json=payload)


def metabase_export_card(
    client: MetabaseClient,
    card_id: int,
    format: str = "csv",
) -> bytes:
    """Exporta los resultados de una card en CSV, XLSX o JSON.

    Endpoint: POST /api/card/:id/query/:format.

    Args:
        client: Cliente autenticado.
        card_id: ID de la card.
        format: Formato de exportación: "csv", "xlsx" o "json".

    Returns:
        bytes con el contenido del archivo exportado.

    Example:
        >>> data = metabase_export_card(client, 42, format="csv")
        >>> with open("export.csv", "wb") as f:
        ...     f.write(data)
    """
    resp = client._http.request("POST", f"/api/card/{card_id}/query/{format}")
    resp.raise_for_status()
    return resp.content


def metabase_create_model(
    client: MetabaseClient,
    name: str,
    sql: str,
    database_id: int,
    collection_id: int = 0,
    description: str = "",
) -> dict:
    """Crea un modelo (card tipo model) que otras cards pueden referenciar.

    Un modelo es una card con type="model". Otras cards MBQL pueden usarlo
    como fuente con source-table: "card__<model_id>".

    Endpoint: POST /api/card con type="model".

    Args:
        client: Cliente autenticado.
        name: Nombre del modelo.
        sql: Query SQL del modelo.
        database_id: ID de la database en Metabase.
        collection_id: Coleccion destino. 0 = root.
        description: Descripcion opcional.

    Returns:
        Dict con el modelo creado (id, name, type="model").

    Example:
        >>> model = metabase_create_model(client, "supply_orders_base",
        ...     "SELECT * FROM supply_orders WHERE ...", database_id=6)
        >>> # Usar en otra card MBQL:
        >>> card = metabase_create_card(client, "Revenue", {
        ...     "database": 6, "type": "query",
        ...     "query": {"source-table": f"card__{model['id']}"}
        ... })
    """
    body: dict = {
        "name": name,
        "type": "model",
        "dataset_query": {
            "database": database_id,
            "type": "native",
            "native": {"query": sql},
        },
        "display": "table",
        "visualization_settings": {},
    }
    if collection_id > 0:
        body["collection_id"] = collection_id
    if description:
        body["description"] = description
    return client.request("POST", "/api/card", json=body)
