import json
import logging
from typing import List, Optional

from fastapi import APIRouter, Depends, HTTPException, Query

from app.core.auth_dependencies import get_current_user, require_permission
from app.core.database import execute_query
from app.modules.links.backend.service import (
    build_action_result,
    get_link_category_ids,
    get_relevant_links,
    log_access,
    update_link_categories,
)
from app.modules.links.models.schemas import (
    Link,
    LinkActionLogCreate,
    LinkActionResult,
    LinkCategory,
    LinkCategoryCreate,
    LinkCreate,
    LinkLatestStatus,
    LinkVaultResolveRequest,
    LinkVaultResolveResponse,
    LinkUpdate,
    RelevantLink,
)
from app.services.vaultwarden_service import resolve_vault_credentials

logger = logging.getLogger(__name__)
router = APIRouter()


def _with_categories(link_row: dict) -> dict:
    out = dict(link_row)
    out["vault_item_ids"] = out.get("vault_item_ids") or []
    out["category_ids"] = get_link_category_ids(int(out["id"]))
    return out


@router.get("/links/health")
async def links_health():
    execute_query("SELECT 1", ())
    return {"status": "healthy", "service": "links-module"}


@router.get("/links/categories", response_model=List[LinkCategory])
async def list_categories(current_user: dict = Depends(require_permission("links.read"))):
    del current_user
    rows = execute_query(
        "SELECT * FROM link_categories ORDER BY sort_order ASC, name ASC",
        (),
    ) or []
    return rows


@router.post("/links/categories", response_model=LinkCategory)
async def create_category(
    payload: LinkCategoryCreate,
    current_user: dict = Depends(require_permission("links.create")),
):
    del current_user
    rows = execute_query(
        """
        INSERT INTO link_categories (name, icon, sort_order)
        VALUES (%s, %s, %s)
        RETURNING *
        """,
        (payload.name, payload.icon, payload.sort_order),
    )
    return rows[0]


@router.get("/links", response_model=List[Link])
async def list_links(
    q: Optional[str] = Query(None),
    customer_id: Optional[int] = Query(None),
    case_id: Optional[int] = Query(None),
    hardware_id: Optional[int] = Query(None),
    category_id: Optional[int] = Query(None),
    is_favorite: Optional[bool] = Query(None),
    current_user: dict = Depends(require_permission("links.read")),
):
    del current_user

    query = """
        SELECT l.*
        FROM links l
        WHERE l.deleted_at IS NULL
    """
    params: List[object] = []

    if q:
        query += " AND (l.name ILIKE %s OR l.url ILIKE %s OR l.host ILIKE %s)"
        term = f"%{q}%"
        params.extend([term, term, term])
    if customer_id is not None:
        query += " AND l.customer_id = %s"
        params.append(customer_id)
    if case_id is not None:
        query += " AND l.case_id = %s"
        params.append(case_id)
    if hardware_id is not None:
        query += " AND l.hardware_id = %s"
        params.append(hardware_id)
    if is_favorite is not None:
        query += " AND l.is_favorite = %s"
        params.append(is_favorite)
    if category_id is not None:
        query += " AND EXISTS (SELECT 1 FROM link_category_map lcm WHERE lcm.link_id = l.id AND lcm.category_id = %s)"
        params.append(category_id)

    query += " ORDER BY l.is_critical DESC, l.updated_at DESC"
    rows = execute_query(query, tuple(params) if params else ()) or []

    return [_with_categories(row) for row in rows]


@router.get("/links/status/latest", response_model=List[LinkLatestStatus])
async def list_latest_link_status(
    link_id: Optional[int] = Query(None),
    current_user: dict = Depends(require_permission("links.read")),
):
    del current_user

    rows = execute_query(
        """
        SELECT DISTINCT ON (ls.link_id)
            ls.link_id,
            ls.status,
            ls.checked_at,
            ls.details
        FROM link_status_checks ls
        WHERE (%s IS NULL OR ls.link_id = %s)
        ORDER BY ls.link_id, ls.checked_at DESC
        """,
        (link_id, link_id),
    ) or []
    return rows


@router.get("/links/{link_id}", response_model=Link)
async def get_link(link_id: int, current_user: dict = Depends(require_permission("links.read"))):
    del current_user
    rows = execute_query("SELECT * FROM links WHERE id = %s AND deleted_at IS NULL", (link_id,))
    if not rows:
        raise HTTPException(status_code=404, detail="Link not found")
    return _with_categories(rows[0])


@router.post("/links", response_model=Link)
async def create_link(payload: LinkCreate, current_user: dict = Depends(require_permission("links.create"))):
    rows = execute_query(
        """
        INSERT INTO links (
            name, description, type, url, host, port, username, icon, color,
            customer_id, case_id, hardware_id,
            vault_item_id, vault_item_ids,
            is_critical, is_favorite, environment
        )
        VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s::jsonb, %s, %s, %s)
        RETURNING *
        """,
        (
            payload.name,
            payload.description,
            payload.type.value,
            payload.url,
            payload.host,
            payload.port,
            payload.username,
            payload.icon,
            payload.color,
            payload.customer_id,
            payload.case_id,
            payload.hardware_id,
            payload.vault_item_id,
            json.dumps(payload.vault_item_ids),
            payload.is_critical,
            payload.is_favorite,
            payload.environment.value,
        ),
    )
    created = rows[0]

    update_link_categories(int(created["id"]), payload.category_ids)

    execute_query(
        """
        INSERT INTO links_audit_log (link_id, event_type, actor_user_id, changes)
        VALUES (%s, %s, %s, %s::jsonb)
        """,
        (created["id"], "created", current_user["id"], json.dumps({"name": payload.name})),
    )

    return _with_categories(created)


@router.patch("/links/{link_id}", response_model=Link)
async def update_link(
    link_id: int,
    payload: LinkUpdate,
    current_user: dict = Depends(require_permission("links.update")),
):
    fields = payload.model_dump(exclude_unset=True)
    category_ids = fields.pop("category_ids", None)

    updates = []
    params: List[object] = []

    for field_name, value in fields.items():
        if field_name == "type" and value is not None:
            updates.append("type = %s")
            params.append(value.value)
        elif field_name == "environment" and value is not None:
            updates.append("environment = %s")
            params.append(value.value)
        elif field_name == "vault_item_ids" and value is not None:
            updates.append("vault_item_ids = %s::jsonb")
            params.append(json.dumps(value))
        else:
            updates.append(f"{field_name} = %s")
            params.append(value)

    if updates:
        updates.append("updated_at = NOW()")
        params.append(link_id)
        query = f"UPDATE links SET {', '.join(updates)} WHERE id = %s AND deleted_at IS NULL RETURNING *"
        rows = execute_query(query, tuple(params)) or []
        if not rows:
            raise HTTPException(status_code=404, detail="Link not found")
        updated = rows[0]
    else:
        rows = execute_query("SELECT * FROM links WHERE id = %s AND deleted_at IS NULL", (link_id,))
        if not rows:
            raise HTTPException(status_code=404, detail="Link not found")
        updated = rows[0]

    if category_ids is not None:
        update_link_categories(link_id, category_ids)

    execute_query(
        """
        INSERT INTO links_audit_log (link_id, event_type, actor_user_id, changes)
        VALUES (%s, %s, %s, %s::jsonb)
        """,
        (link_id, "updated", current_user["id"], json.dumps(fields or {"category_ids": category_ids})),
    )

    return _with_categories(updated)


@router.delete("/links/{link_id}")
async def delete_link(link_id: int, current_user: dict = Depends(require_permission("links.delete"))):
    rows = execute_query(
        "UPDATE links SET deleted_at = NOW(), updated_at = NOW() WHERE id = %s AND deleted_at IS NULL RETURNING id",
        (link_id,),
    ) or []
    if not rows:
        raise HTTPException(status_code=404, detail="Link not found")

    execute_query(
        """
        INSERT INTO links_audit_log (link_id, event_type, actor_user_id, changes)
        VALUES (%s, %s, %s, %s::jsonb)
        """,
        (link_id, "deleted", current_user["id"], json.dumps({"deleted": True})),
    )

    return {"status": "deleted", "id": link_id}


@router.get("/links/cases/{case_id}/relevant", response_model=List[RelevantLink])
async def case_relevant_links(
    case_id: int,
    limit: int = Query(50, ge=1, le=200),
    current_user: dict = Depends(require_permission("links.read")),
):
    del current_user
    return get_relevant_links(case_id, limit=limit)


@router.post("/links/{link_id}/access", response_model=LinkActionResult)
async def access_link(
    link_id: int,
    payload: LinkActionLogCreate,
    current_user: dict = Depends(require_permission("links.use")),
):
    rows = execute_query("SELECT * FROM links WHERE id = %s AND deleted_at IS NULL", (link_id,)) or []
    if not rows:
        raise HTTPException(status_code=404, detail="Link not found")

    link_row = rows[0]
    action_result = build_action_result(link_row, payload.action_type)

    log_access(
        link_id=link_id,
        user_id=current_user["id"],
        action_type=payload.action_type,
        case_id=payload.case_id,
        customer_id=payload.customer_id,
        metadata=payload.metadata,
    )

    return action_result


@router.post("/links/{link_id}/vault/resolve", response_model=LinkVaultResolveResponse)
async def resolve_link_vault(
    link_id: int,
    payload: LinkVaultResolveRequest,
    current_user: dict = Depends(require_permission("links.use")),
):
    rows = execute_query("SELECT * FROM links WHERE id = %s AND deleted_at IS NULL", (link_id,)) or []
    if not rows:
        raise HTTPException(status_code=404, detail="Link not found")

    link_row = rows[0]
    fallback_item_ids = link_row.get("vault_item_ids") or []
    if not isinstance(fallback_item_ids, list):
        fallback_item_ids = []

    result = await resolve_vault_credentials(
        preferred_item_id=payload.item_id or link_row.get("vault_item_id"),
        fallback_item_ids=[str(item) for item in fallback_item_ids if item],
        search_hint=payload.search_hint or link_row.get("host") or link_row.get("url") or link_row.get("name"),
    )

    log_access(
        link_id=link_id,
        user_id=current_user["id"],
        action_type="vault.resolve",
        case_id=link_row.get("case_id"),
        customer_id=link_row.get("customer_id"),
        metadata={
            "status": result.get("status"),
            "configured": result.get("configured"),
            "checked_item_ids": result.get("checked_item_ids") or [],
        },
    )

    return result


@router.post("/links/health/run")
async def run_links_health_check(
    current_user: dict = Depends(require_permission("links.diagnose")),
):
    del current_user
    from app.modules.links.jobs.dead_link_check import check_links_health

    result = await check_links_health()
    return {"status": "ok", "result": result}
