"""Linear integration API routes for Mixer Studio."""

from __future__ import annotations

import json
import logging
import os

from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel

from mixersystem.studio.config import StudioConfig
from mixersystem.studio.process_manager import ProcessManager
from mixersystem.studio.routes import get_config, get_process_manager
from mixersystem.studio.session_paths import resolve_session_path

# Lazy-loaded at module level to avoid circular imports at import time
_linear_sync = None
_update_session_json = None


def _get_linear_sync():
    global _linear_sync
    if _linear_sync is None:
        from mixersystem.data import linear_sync
        _linear_sync = linear_sync
    return _linear_sync


def _get_update_session_json():
    global _update_session_json
    if _update_session_json is None:
        from mixersystem.data.repository import update_session_json
        _update_session_json = update_session_json
    return _update_session_json

log = logging.getLogger(__name__)

router = APIRouter(prefix="/api/v1/linear", tags=["linear"])


def _resolve_session_path_or_400(config: StudioConfig, session_name: str):
    try:
        return resolve_session_path(config.sessions_dir, session_name)
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e)) from e


def _linear_settings() -> dict:
    """Return the full linear settings dict from settings.json."""
    try:
        from mixersystem.data.repository import load_settings
        settings = load_settings()
    except (ImportError, OSError, json.JSONDecodeError):
        return {}
    linear = settings.get("linear", {}) if isinstance(settings.get("linear"), dict) else {}
    return linear


def _resolve_api_key() -> str | None:
    """Resolve Linear API key from environment (loaded from .env at startup)."""
    key = os.environ.get("LINEAR_API_KEY", "")
    return key.strip() if key and key.strip() else None


def _resolve_prefix() -> str | None:
    linear = _linear_settings()
    prefix = linear.get("team_prefix") or linear.get("prefix")
    return prefix.strip() if isinstance(prefix, str) and prefix.strip() else None


@router.get("/config")
def get_linear_config():
    """Check if Linear is configured."""
    api_key = _resolve_api_key()
    prefix = _resolve_prefix()
    return {
        "configured": bool(api_key and prefix),
        "team_prefix": prefix or "",
    }


@router.get("/issues/{issue_id}")
def fetch_issue(issue_id: str):
    """Fetch a single Linear issue for preview."""
    api_key = _resolve_api_key()
    if not api_key:
        raise HTTPException(status_code=400, detail="LINEAR_API_KEY not set. Add it to .env in your project root.")

    ls = _get_linear_sync()
    try:
        data = ls._graphql(ls.LOAD_QUERY, {"id": issue_id}, api_key)
    except (OSError, ValueError, RuntimeError) as e:
        raise HTTPException(status_code=502, detail=str(e)) from e

    issue = data.get("issue")
    if not issue:
        raise HTTPException(status_code=404, detail=f"Issue '{issue_id}' not found")

    return {
        "id": issue.get("identifier") or issue_id,
        "title": issue.get("title", ""),
        "description": issue.get("description", ""),
        "state": issue.get("state", {}).get("name", ""),
    }


class ImportRequest(BaseModel):
    issue_id: str
    session_name: str


@router.post("/import")
async def import_from_linear(
    body: ImportRequest,
    pm: ProcessManager = Depends(get_process_manager),
    config: StudioConfig = Depends(get_config),
):
    """Import a Linear issue into a session and run the task workflow."""
    api_key = _resolve_api_key()
    if not api_key:
        raise HTTPException(status_code=400, detail="LINEAR_API_KEY not set. Add it to .env in your project root.")

    # Fetch issue
    ls = _get_linear_sync()
    try:
        data = ls._graphql(ls.LOAD_QUERY, {"id": body.issue_id}, api_key)
    except (OSError, ValueError, RuntimeError) as e:
        raise HTTPException(status_code=502, detail=str(e)) from e

    issue = data.get("issue")
    if not issue:
        raise HTTPException(status_code=404, detail=f"Issue '{body.issue_id}' not found")

    identifier = issue.get("identifier") or body.issue_id
    title = issue.get("title", "Untitled")
    description = issue.get("description", "")
    status = issue.get("state", {}).get("name", "")

    # Rename session folder to the issue identifier
    source = _resolve_session_path_or_400(config, body.session_name)
    target = _resolve_session_path_or_400(config, identifier)

    if source.name != identifier:
        if target.exists():
            raise HTTPException(
                status_code=409,
                detail=f"Session folder '{identifier}' already exists",
            )
        if source.exists():
            source.rename(target)
        else:
            target.mkdir(parents=True, exist_ok=True)
    else:
        target.mkdir(parents=True, exist_ok=True)

    new_session_name = identifier

    # Write Linear metadata to session.json
    update_session_json = _get_update_session_json()
    linear_meta = {"id": identifier, "name": title}
    if status:
        linear_meta["status"] = status
    assignee = issue.get("assignee")
    if assignee:
        linear_meta["assignee"] = f"{assignee['id']} ({assignee['name']})"
    cycle = issue.get("cycle")
    if cycle:
        linear_meta["cycle"] = f"{cycle['id']} ({cycle['name']})"

    update_session_json(str(target), modules=["all"], modules_locked=True, linear=linear_meta)

    # Build instructions from the Linear issue content
    instructions = (
        "This is a Linear issue. Preserve its content faithfully, structuring it "
        "according to the task template but keeping all original information.\n\n"
        f"# {title}\n\n"
        f"## Description\n\n{description or '(no description)'}\n"
    )

    # Start task workflow
    folder_rel = f".mixer/sessions/{new_session_name}"
    try:
        run = await pm.start_workflow("task", folder_rel, {"instructions": instructions})
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e)) from e

    return {
        "issue_id": identifier,
        "new_session_name": new_session_name,
        "run_id": run.run_id,
    }


class ExportRequest(BaseModel):
    session_name: str


@router.post("/export")
def export_to_linear(
    body: ExportRequest,
    config: StudioConfig = Depends(get_config),
):
    """Export task.md to Linear (create or update)."""
    api_key = _resolve_api_key()
    if not api_key:
        raise HTTPException(status_code=400, detail="LINEAR_API_KEY not set. Add it to .env in your project root.")

    folder = _resolve_session_path_or_400(config, body.session_name)
    task_path = folder / "task.md"
    if not task_path.is_file():
        raise HTTPException(status_code=404, detail="task.md not found in session")

    prefix = _resolve_prefix()
    is_linear_issue = bool(
        prefix and folder.name.upper().startswith(f"{prefix.upper()}-")
    )

    ls = _get_linear_sync()
    update_session_json = _get_update_session_json()

    if is_linear_issue:
        # Update existing
        try:
            ls.update(
                issue_id=folder.name,
                task_path=str(task_path),
                api_key=api_key,
            )
        except (OSError, ValueError, RuntimeError) as e:
            raise HTTPException(status_code=502, detail=str(e)) from e
        return {"action": "updated", "issue_id": folder.name}
    else:
        # Push new
        try:
            new_issue_id = ls.push(
                task_path=str(task_path),
                api_key=api_key,
            )
        except (OSError, ValueError, RuntimeError) as e:
            raise HTTPException(status_code=502, detail=str(e)) from e
        update_session_json(str(folder), linear={"id": new_issue_id})

        # Rename folder to issue ID
        target = _resolve_session_path_or_400(config, new_issue_id)
        new_session_name = new_issue_id
        if not target.exists():
            folder.rename(target)
        else:
            new_session_name = folder.name

        return {
            "action": "created",
            "issue_id": new_issue_id,
            "new_session_name": new_session_name,
        }
