# backend/app/routers/connectors.py — phase-13 외부 연동 API
# prefix 없음. main.py 에서 include_router(prefix="/api").
import uuid

from fastapi import APIRouter, BackgroundTasks, Depends, File, HTTPException, UploadFile
from fastapi.responses import RedirectResponse
from sqlmodel import Session, select

from ..config import get_settings
from ..connectors import oauth as oauthlib
from ..connectors.registry import ConnectorRegistry
from ..db import engine, get_session
from ..models import ConnectorAccount, ConnectorAccountLog, ConnState, OAuthState
from ..schemas import (
    ConnectorProviderOut,
    ConnectorStatusOut,
    ImportResultOut,
    OAuthStartOut,
    SyncResultOut,
)

router = APIRouter()

# phase-16: '계정 추가'가 보여줄 OAuth provider 카탈로그.
# (domain, provider, label, settings client_id 필드명)
_OAUTH_PROVIDERS = [
    ("mail", "gmail", "Gmail", "google_client_id"),
    ("mail", "outlook", "Outlook", "microsoft_client_id"),
    ("calendar", "google_calendar", "Google 캘린더", "google_client_id"),
    ("calendar", "outlook_calendar", "Outlook 캘린더", "microsoft_client_id"),
]


@router.get("/connectors", response_model=list[ConnectorStatusOut])
def list_connectors(domain: str | None = None, s: Session = Depends(get_session)):
    rows = ConnectorRegistry.accounts(s, domain)
    return [ConnectorStatusOut.from_account(a, ConnectorRegistry.mode_for(a.domain)) for a in rows]


@router.get("/connectors/providers", response_model=list[ConnectorProviderOut])
def list_providers(domain: str | None = None):
    """OAuth로 추가 가능한 provider 목록 + 구성 여부(client_id 설정 시 configured=true)."""
    cfg = get_settings()
    out = []
    for dom, provider, label, field in _OAUTH_PROVIDERS:
        if domain and dom != domain:
            continue
        out.append(
            ConnectorProviderOut(
                domain=dom,
                provider=provider,
                label=label,
                configured=bool(getattr(cfg, field, "")),
            )
        )
    return out


@router.post("/connectors/{account_id}/sync", response_model=SyncResultOut)
def sync_one(account_id: str, full: bool = False, s: Session = Depends(get_session)):
    a = s.get(ConnectorAccount, account_id)
    if not a:
        raise HTTPException(404, "connector account not found")
    res = ConnectorRegistry.get(s, a).sync(s, full=full)
    return SyncResultOut(**res.__dict__)


@router.post("/connectors/sync-all", response_model=list[SyncResultOut])
def sync_all(s: Session = Depends(get_session)):
    out = []
    for a in ConnectorRegistry.accounts(s):
        if a.state == ConnState.disconnected:  # 연결 안 된 계정은 건너뜀
            continue
        out.append(SyncResultOut(**ConnectorRegistry.get(s, a).sync(s).__dict__))
    return out


# ── OAuth ──
def _with_query(path: str, extra: str) -> str:
    """redirect_after 경로에 쿼리 안전 append('/settings?tab=mail' + 'connect=ok')."""
    sep = "&" if "?" in path else "?"
    return f"{path}{sep}{extra}"


@router.get("/connectors/oauth/start", response_model=OAuthStartOut)
def oauth_start(
    domain: str, provider: str, redirect_after: str = "/life", s: Session = Depends(get_session)
):
    cfg = get_settings()
    if provider in ("gmail", "google_calendar") and not cfg.google_client_id:
        raise HTTPException(400, "google_client_id 미설정 — real 연동 비활성")
    if provider in ("outlook", "outlook_calendar") and not cfg.microsoft_client_id:
        raise HTTPException(400, "microsoft_client_id 미설정 — real 연동 비활성")
    if provider == "notion" and not cfg.notion_client_id:
        raise HTTPException(400, "notion_client_id 미설정 — real 연동 비활성")
    url = oauthlib.start_oauth(s, domain, provider, redirect_after)
    return OAuthStartOut(authorize_url=url)


def _initial_sync(account_id: str) -> None:
    """연결 직후 초기 full sync — 백그라운드 실행(대량 메일도 콜백 응답을 막지 않음)."""
    with Session(engine) as bs:
        acct = bs.get(ConnectorAccount, account_id)
        if not acct:
            return
        targets = [acct]
        # 메일과 함께 연결된 캘린더 계정(같은 이메일)도 초기 sync.
        sibling_provider = {"gmail": "google_calendar", "outlook": "outlook_calendar"}.get(
            acct.provider
        )
        if sibling_provider and acct.external_account_id:
            sib = bs.exec(
                select(ConnectorAccount).where(
                    ConnectorAccount.provider == sibling_provider,
                    ConnectorAccount.external_account_id == acct.external_account_id,
                )
            ).first()
            if sib:
                targets.append(sib)
        for a in targets:
            try:
                ConnectorRegistry.get(bs, a).sync(bs, full=True)
            except Exception:
                pass


@router.get("/connectors/oauth/callback")
def oauth_callback(
    code: str, state: str, bg: BackgroundTasks, s: Session = Depends(get_session)
):
    origin = get_settings().frontend_origin
    os_row = s.get(OAuthState, state)
    redirect_after = os_row.redirect_after if os_row else "/life"
    try:
        acct = oauthlib.finish_oauth(s, code, state)
    except Exception:
        return RedirectResponse(url=f"{origin}{_with_query(redirect_after, 'connect=error')}")
    # 초기 sync 는 백그라운드로(받은편지함이 크면 수십 초 소요 → 콜백은 즉시 리다이렉트).
    bg.add_task(_initial_sync, acct.id)
    q = f"connect=ok&domain={acct.domain.value}&provider={acct.provider}"
    return RedirectResponse(url=f"{origin}{_with_query(redirect_after, q)}")


@router.post("/connectors/{account_id}/disconnect", response_model=ConnectorStatusOut)
def disconnect(account_id: str, s: Session = Depends(get_session)):
    a = s.get(ConnectorAccount, account_id)
    if not a:
        raise HTTPException(404, "not found")
    a.state = ConnState.disconnected
    a.token_enc = ""
    a.last_label = "연결 안 됨"
    s.add(a)
    s.add(
        ConnectorAccountLog(
            id="cl-" + uuid.uuid4().hex[:8],
            account_id=a.id,
            action="disconnected",
            detail="사용자 해제",
        )
    )
    s.commit()
    return ConnectorStatusOut.from_account(a, ConnectorRegistry.mode_for(a.domain))


# ── 로컬 우선: 수동 임포트(CSV / .ics / HealthKit export) ──
@router.post("/connectors/{account_id}/import", response_model=ImportResultOut)
async def import_file(
    account_id: str, file: UploadFile = File(...), s: Session = Depends(get_session)
):
    a = s.get(ConnectorAccount, account_id)
    if not a:
        raise HTTPException(404, "not found")
    conn = ConnectorRegistry.get(s, a)
    if not hasattr(conn, "import_bytes"):
        raise HTTPException(400, "이 커넥터는 파일 임포트를 지원하지 않아요")
    content = await file.read()
    res = conn.import_bytes(s, content, filename=file.filename or "")
    return ImportResultOut(**res)
