"""
Errors BFF API - #40 Sentry 錯誤數據 API
========================================
為前端提供 Sentry 錯誤追蹤數據

Phase 10: Sentry + OpenClaw + UI 整合
建立時間: 2026-03-26 18:45 (台北時區)
建立者: Claude Code (#40 BFF API)
重構: 2026-03-26 21:20 (P2 架構改善 - SentryService 抽取)

功能:
1. 列出近期錯誤
2. 取得錯誤詳情
3. 取得錯誤趨勢
4. 觸發 AI 分析

遵循 leWOOOgo 積木化原則:
- Router 層只做 HTTP 轉發
- 不直接存取 Redis/DB/外部 API
- 業務邏輯委託給 Service 層
"""

from fastapi import APIRouter, HTTPException, Query
from pydantic import BaseModel, Field

from src.core.logging import get_logger
from src.services.sentry_service import get_sentry_service
from src.utils.timezone import now_taipei

logger = get_logger(__name__)

router = APIRouter(prefix="/errors", tags=["Errors"])


# =============================================================================
# Response Models
# =============================================================================


class SentryIssue(BaseModel):
    """Sentry Issue 簡化模型"""

    id: str = Field(description="Issue ID")
    short_id: str = Field(description="短 ID (如 AWOOOI-123)")
    title: str = Field(description="錯誤標題")
    culprit: str | None = Field(None, description="錯誤來源 (函數/檔案)")
    level: str = Field(description="嚴重度 (error, warning, info)")
    status: str = Field(description="狀態 (unresolved, resolved, ignored)")
    count: int = Field(description="發生次數")
    user_count: int = Field(default=0, description="影響用戶數")
    first_seen: str = Field(description="首次發生時間")
    last_seen: str = Field(description="最後發生時間")
    permalink: str | None = Field(None, description="Sentry 詳情連結")


class IssueListResponse(BaseModel):
    """Issue 列表回應"""

    issues: list[SentryIssue]
    total: int
    has_more: bool


class IssueTrend(BaseModel):
    """錯誤趨勢數據點"""

    timestamp: str
    count: int


class TrendResponse(BaseModel):
    """趨勢回應"""

    period: str  # 24h, 7d, 30d
    data: list[IssueTrend]
    total_count: int
    change_percent: float  # 與前一週期相比


class IssueStats(BaseModel):
    """錯誤統計"""

    total_issues: int = 0
    unresolved_issues: int = 0
    error_count_24h: int = 0
    critical_count: int = 0
    projects: list[str] = []


# =============================================================================
# API Endpoints
# =============================================================================


@router.get("/stats", response_model=IssueStats)
async def get_error_stats() -> IssueStats:
    """
    取得錯誤統計概覽

    返回:
    - 總 Issue 數
    - 未解決 Issue 數
    - 24 小時內錯誤數
    - 重大錯誤數
    """
    sentry = get_sentry_service()

    # 取得組織專案列表
    projects = await sentry.list_projects()

    if not projects:
        return IssueStats()

    project_slugs = [p.get("slug", "") for p in projects if isinstance(p, dict)]

    # 取得未解決 Issues
    issues = await sentry.list_issues(query="is:unresolved", limit=100)
    unresolved_count = len(issues) if issues else 0

    # 統計 24h 內的錯誤
    issues_24h = await sentry.list_issues(query="is:unresolved age:-24h", limit=100)
    error_24h = len(issues_24h) if issues_24h else 0

    # 統計 critical/fatal
    critical_issues = await sentry.list_issues(query="is:unresolved level:fatal", limit=100)
    critical_count = len(critical_issues) if critical_issues else 0

    return IssueStats(
        total_issues=unresolved_count,
        unresolved_issues=unresolved_count,
        error_count_24h=error_24h,
        critical_count=critical_count,
        projects=project_slugs,
    )


@router.get("/issues", response_model=IssueListResponse)
async def list_issues(
    project: str | None = Query(default=None, description="專案 slug"),
    status: str = Query(default="unresolved", description="狀態過濾"),
    level: str | None = Query(default=None, description="嚴重度過濾 (error, warning)"),
    limit: int = Query(default=25, ge=1, le=100, description="每頁數量"),
    cursor: str | None = Query(default=None, description="分頁游標"),
) -> IssueListResponse:
    """
    列出 Sentry Issues

    支援過濾:
    - status: unresolved, resolved, ignored
    - level: fatal, error, warning, info
    """
    sentry = get_sentry_service()

    query_parts = [f"is:{status}"]
    if level:
        query_parts.append(f"level:{level}")

    raw_issues = await sentry.list_issues(
        project=project,
        query=" ".join(query_parts),
        limit=limit,
        cursor=cursor,
    )

    if not raw_issues:
        return IssueListResponse(issues=[], total=0, has_more=False)

    issues = []
    for issue in raw_issues:
        if isinstance(issue, dict):
            issues.append(
                SentryIssue(
                    id=issue.get("id", ""),
                    short_id=issue.get("shortId", ""),
                    title=issue.get("title", "Unknown Error"),
                    culprit=issue.get("culprit"),
                    level=issue.get("level", "error"),
                    status=issue.get("status", "unresolved"),
                    count=issue.get("count", 0),
                    user_count=issue.get("userCount", 0),
                    first_seen=issue.get("firstSeen", ""),
                    last_seen=issue.get("lastSeen", ""),
                    permalink=issue.get("permalink"),
                )
            )

    return IssueListResponse(
        issues=issues,
        total=len(issues),
        has_more=len(issues) >= limit,
    )


@router.get("/issues/{issue_id}")
async def get_issue_detail(issue_id: str) -> dict:
    """
    取得 Issue 詳情

    包含:
    - 錯誤堆疊
    - 影響用戶
    - 錯誤上下文
    """
    sentry = get_sentry_service()

    issue = await sentry.get_issue(issue_id)

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

    # 取得最新事件
    events = await sentry.get_issue_events(issue_id, limit=1)
    latest_event = events[0] if events and len(events) > 0 else None

    return {
        "issue": issue,
        "latest_event": latest_event,
        "sentry_url": sentry.get_issue_url(issue_id),
    }


@router.get("/trends", response_model=TrendResponse)
async def get_error_trends(
    project: str | None = Query(default=None, description="專案 slug"),
    period: str = Query(default="24h", description="時間範圍 (24h, 7d, 30d)"),
) -> TrendResponse:
    """
    取得錯誤趨勢

    返回指定時間範圍內的錯誤數量趨勢
    """
    sentry = get_sentry_service()

    # 計算時間範圍
    period_map = {
        "24h": ("1h", 24),
        "7d": ("1d", 7),
        "30d": ("1d", 30),
    }

    interval, points = period_map.get(period, ("1h", 24))

    # Sentry Stats API
    stats = await sentry.get_project_stats(
        project=project,
        stat="received",
        resolution=interval,
    )

    if not stats:
        return TrendResponse(
            period=period,
            data=[],
            total_count=0,
            change_percent=0.0,
        )

    # 轉換數據格式
    data = []
    total = 0

    for point in stats[-points:] if len(stats) > points else stats:
        if isinstance(point, list) and len(point) >= 2:
            timestamp, count = point[0], point[1]
            data.append(IssueTrend(timestamp=str(timestamp), count=count))
            total += count

    # 計算變化百分比 (與前一週期比較)
    prev_total = sum(p[1] for p in stats[-points * 2 : -points] if isinstance(p, list) and len(p) >= 2)
    change = ((total - prev_total) / prev_total * 100) if prev_total > 0 else 0.0

    return TrendResponse(
        period=period,
        data=data,
        total_count=total,
        change_percent=round(change, 1),
    )


# =============================================================================
# UX Audit (Phase 19 - Session Replay 整合)
# 2026-03-29 Claude Code - 自動化 UI/UX 監控
# =============================================================================


class UXAuditResponse(BaseModel):
    """UX 審計回應"""

    replays_with_errors: int = Field(description="有錯誤的 Replay 數")
    rage_clicks: int = Field(description="憤怒點擊數")
    dead_clicks: int = Field(description="死亡點擊數")
    ui_errors: int = Field(description="UI 錯誤數")
    health_score: str = Field(description="健康度評分 (good/moderate/poor)")
    details: list[dict] = Field(default=[], description="問題詳情列表")
    replay_dashboard_url: str = Field(description="Sentry Replay Dashboard 連結")


@router.get("/ux-audit", response_model=UXAuditResponse)
async def get_ux_audit(
    project: str | None = Query(default=None, description="專案 slug"),
) -> UXAuditResponse:
    """
    UX 審計 API

    使用 Sentry Session Replay 數據自動檢測 UI/UX 問題:
    - 有錯誤的 Replay
    - 憤怒點擊 (Rage Clicks) - 使用者快速連擊同一位置
    - 死亡點擊 (Dead Clicks) - 點擊無反應的元素
    - UI 相關錯誤 (TypeError, render errors)

    Returns:
        UXAuditResponse: 包含問題統計、健康度評分和詳情
    """
    sentry = get_sentry_service()
    summary = await sentry.get_ux_audit_summary(project=project)

    # 計算健康度
    total_issues = summary["replays_with_errors"] + summary["ui_errors"]
    if total_issues > 10:
        health_score = "poor"
    elif total_issues > 3:
        health_score = "moderate"
    else:
        health_score = "good"

    logger.info(
        "ux_audit_completed",
        health_score=health_score,
        replays_with_errors=summary["replays_with_errors"],
        rage_clicks=summary["rage_clicks"],
        dead_clicks=summary["dead_clicks"],
        ui_errors=summary["ui_errors"],
    )

    return UXAuditResponse(
        replays_with_errors=summary["replays_with_errors"],
        rage_clicks=summary["rage_clicks"],
        dead_clicks=summary["dead_clicks"],
        ui_errors=summary["ui_errors"],
        health_score=health_score,
        details=summary["details"],
        replay_dashboard_url=f"{sentry.base_url}/organizations/{sentry.org}/replays/",
    )


@router.post("/issues/{issue_id}/analyze")
async def trigger_ai_analysis(
    issue_id: str,
    project: str | None = Query(default=None, description="專案 slug"),
) -> dict:
    """
    觸發 AI 分析

    呼叫 OpenClaw 對指定 Issue 進行根因分析
    """
    sentry = get_sentry_service()

    # 取得 Issue 詳情
    issue = await sentry.get_issue(issue_id)

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

    # 取得最新事件 (含堆疊)
    events = await sentry.get_issue_events(issue_id, limit=1, full=True)

    if not events:
        raise HTTPException(
            status_code=404,
            detail=f"No events found for issue {issue_id}",
        )

    latest_event = events[0]

    # 提取分析所需資料
    title = issue.get("title", "Unknown Error")
    level = issue.get("level", "error")
    culprit = issue.get("culprit", "")
    count = issue.get("count", 0)
    stacktrace = _extract_stacktrace(latest_event)
    context = _extract_context(latest_event)

    logger.info(
        "error_analysis_triggered",
        issue_id=issue_id,
        title=title,
    )

    # 呼叫 Error Analyzer Service (#39)
    from src.services.error_analyzer_service import get_error_analyzer_service

    analyzer = get_error_analyzer_service()
    result, provider, success = await analyzer.analyze_error(
        issue_id=issue_id,
        title=title,
        level=level,
        culprit=culprit,
        count=count,
        stacktrace=stacktrace,
        context=context,
    )

    sentry_url = sentry.get_issue_url(issue_id)

    if not success or result is None:
        return {
            "status": "failed",
            "issue_id": issue_id,
            "provider": provider,
            "message": "AI analysis failed. Please try again later.",
            "sentry_url": sentry_url,
        }

    return {
        "status": "completed",
        "issue_id": issue_id,
        "provider": provider,
        "analysis": {
            "root_cause": result.root_cause,
            "category": result.category,
            "severity": result.severity,
            "impact_assessment": result.impact_assessment,
            "fix_recommendation": result.fix_recommendation.model_dump(),
            "prevention": [p.model_dump() for p in result.prevention],
            "related_files": result.related_files,
            "confidence": result.confidence,
            "reasoning": result.reasoning,
        },
        "analyzed_at": now_taipei().isoformat(),
        "sentry_url": sentry_url,
    }


# =============================================================================
# Helper Functions (Event Parsing - Router 層合理)
# =============================================================================


def _extract_stacktrace(event: dict) -> str:
    """從事件中提取堆疊追蹤"""
    entries = event.get("entries", [])

    for entry in entries:
        if entry.get("type") == "exception":
            values = entry.get("data", {}).get("values", [])
            if values:
                frames = values[0].get("stacktrace", {}).get("frames", [])
                if frames:
                    # 取最後 10 個 frame
                    recent_frames = frames[-10:]
                    lines = []
                    for f in recent_frames:
                        filename = f.get("filename", "")
                        lineno = f.get("lineNo", "")
                        function = f.get("function", "")
                        lines.append(f"  {filename}:{lineno} in {function}")
                    return "\n".join(lines)

    return "No stacktrace available"


def _extract_context(event: dict) -> dict:
    """從事件中提取上下文"""
    return {
        "browser": event.get("contexts", {}).get("browser", {}),
        "os": event.get("contexts", {}).get("os", {}),
        "device": event.get("contexts", {}).get("device", {}),
        "tags": event.get("tags", []),
        "user": event.get("user", {}),
    }
