from __future__ import annotations

import secrets
from datetime import datetime, timezone, timedelta

from fastapi import APIRouter, Depends, HTTPException, Query
from sqlmodel import Session, select

from app.api.deps import get_session
from app.core.config import get_settings
from app.models.core import ActivityEvent, AppSetting, PlatformAccount, Provider
from app.security.tokens import encrypt_token

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


def _now() -> datetime:
    return datetime.now(timezone.utc)


def _youtube_config() -> dict:
    settings = get_settings()
    redirect_uri = settings.google_redirect_uri or "http://127.0.0.1:8012/api/accounts/youtube/connect/callback"
    return {
        "configured": bool(settings.google_client_id and settings.google_client_secret),
        "client_id_present": bool(settings.google_client_id),
        "client_secret_present": bool(settings.google_client_secret),
        "redirect_uri": redirect_uri,
        "scope": settings.youtube_oauth_scope,
        "default_privacy": settings.youtube_default_privacy,
        "notify_subscribers": settings.youtube_upload_notify_subscribers,
        "category_id": settings.youtube_default_category_id,
        "contains_synthetic_media_default": settings.youtube_contains_synthetic_media_default,
        "dry_run": settings.youtube_upload_dry_run,
    }


def _safe_account(account: PlatformAccount) -> dict:
    data = account.model_dump(mode="json")
    data.pop("encrypted_access_token", None)
    data.pop("encrypted_refresh_token", None)
    data["has_access_token"] = bool(account.encrypted_access_token)
    data["has_refresh_token"] = bool(account.encrypted_refresh_token)
    return data


@router.get("")
def list_accounts(session: Session = Depends(get_session)):
    return {"items": [_safe_account(a) for a in session.exec(select(PlatformAccount)).all()], "youtube": _youtube_config(), "tiktok": {"status": "planned_for_sprint_9", "fake_connect": False}}


@router.get("/youtube/status")
def youtube_status(session: Session = Depends(get_session)):
    accounts = session.exec(select(PlatformAccount).where(PlatformAccount.provider == Provider.youtube)).all()
    return {"config": _youtube_config(), "accounts": [_safe_account(a) for a in accounts]}


@router.get("/youtube/connect/start")
def youtube_connect_start(session: Session = Depends(get_session)):
    cfg = _youtube_config()
    if not cfg["configured"]:
        raise HTTPException(status_code=400, detail={"message": "Google OAuth config missing", "config": cfg})
    settings = get_settings()
    state = secrets.token_urlsafe(32)
    row = session.get(AppSetting, "youtube_oauth_state") or AppSetting(key="youtube_oauth_state")
    row.value_json = {"state": state, "created_at": _now().isoformat()}
    row.updated_at = _now()
    session.add(row); session.commit()
    try:
        from google_auth_oauthlib.flow import Flow

        flow = Flow.from_client_config(
            {
                "web": {
                    "client_id": settings.google_client_id,
                    "client_secret": settings.google_client_secret,
                    "auth_uri": "https://accounts.google.com/o/oauth2/auth",
                    "token_uri": "https://oauth2.googleapis.com/token",
                    "redirect_uris": [cfg["redirect_uri"]],
                }
            },
            scopes=[settings.youtube_oauth_scope],
        )
        flow.redirect_uri = cfg["redirect_uri"]
        authorization_url, returned_state = flow.authorization_url(access_type="offline", include_granted_scopes="true", prompt="consent", state=state)
    except Exception as exc:
        raise HTTPException(status_code=500, detail=f"Could not create OAuth URL: {exc}") from exc
    return {"provider": "youtube", "authorization_url": authorization_url, "state": returned_state, "scope": settings.youtube_oauth_scope, "redirect_uri": cfg["redirect_uri"]}


@router.get("/youtube/connect/callback")
def youtube_connect_callback(code: str | None = None, state: str | None = None, error: str | None = None, session: Session = Depends(get_session)):
    if error:
        raise HTTPException(status_code=400, detail={"message": "Google OAuth returned an error", "error": error})
    if not code or not state:
        raise HTTPException(status_code=400, detail="OAuth code and state are required")
    row = session.get(AppSetting, "youtube_oauth_state")
    expected = (row.value_json or {}).get("state") if row else None
    if not expected or state != expected:
        raise HTTPException(status_code=400, detail="Invalid OAuth state")
    cfg = _youtube_config()
    if not cfg["configured"]:
        raise HTTPException(status_code=400, detail={"message": "Google OAuth config missing", "config": cfg})
    settings = get_settings()
    try:
        from google_auth_oauthlib.flow import Flow

        flow = Flow.from_client_config(
            {
                "web": {
                    "client_id": settings.google_client_id,
                    "client_secret": settings.google_client_secret,
                    "auth_uri": "https://accounts.google.com/o/oauth2/auth",
                    "token_uri": "https://oauth2.googleapis.com/token",
                    "redirect_uris": [cfg["redirect_uri"]],
                }
            },
            scopes=[settings.youtube_oauth_scope],
        )
        flow.redirect_uri = cfg["redirect_uri"]
        flow.fetch_token(code=code)
        creds = flow.credentials
    except Exception as exc:
        raise HTTPException(status_code=400, detail=f"Could not exchange OAuth code: {exc}") from exc
    account = session.exec(select(PlatformAccount).where(PlatformAccount.provider == Provider.youtube, PlatformAccount.external_account_id == "youtube-oauth-account")).first()
    if not account:
        account = PlatformAccount(provider=Provider.youtube, display_name="YouTube Account", external_account_id="youtube-oauth-account", account_type="youtube_channel")
    account.scopes = [settings.youtube_oauth_scope]
    account.encrypted_access_token = encrypt_token(creds.token)
    account.encrypted_refresh_token = encrypt_token(creds.refresh_token)
    account.token_expires_at = creds.expiry or (_now() + timedelta(hours=1))
    account.status = "connected"
    account.last_error = None
    account.updated_at = _now()
    if not session.exec(select(PlatformAccount).where(PlatformAccount.provider == Provider.youtube, PlatformAccount.is_default == True)).first():  # noqa: E712
        account.is_default = True
    session.add(account)
    row.value_json = {"state": None, "used_at": _now().isoformat()}
    session.add(row)
    session.add(ActivityEvent(entity_type="platform_account", entity_id=account.id, event_type="youtube_oauth_connected", label="YouTube account connected", payload_json={"scopes": account.scopes}))
    session.commit(); session.refresh(account)
    return {"ok": True, "account": _safe_account(account)}


@router.post("/youtube/disconnect/{account_id}")
def youtube_disconnect(account_id: str, session: Session = Depends(get_session)):
    account = session.get(PlatformAccount, account_id)
    if not account or account.provider != Provider.youtube:
        raise HTTPException(status_code=404, detail="YouTube account not found")
    account.status = "revoked"
    account.encrypted_access_token = None
    account.encrypted_refresh_token = None
    account.token_expires_at = None
    account.updated_at = _now()
    session.add(account)
    session.add(ActivityEvent(entity_type="platform_account", entity_id=account.id, event_type="youtube_oauth_disconnected", label="YouTube account disconnected", payload_json={}))
    session.commit()
    return {"ok": True, "account": _safe_account(account)}


@router.post("/youtube/reconnect/{account_id}")
def youtube_reconnect(account_id: str, session: Session = Depends(get_session)):
    account = session.get(PlatformAccount, account_id)
    if not account or account.provider != Provider.youtube:
        raise HTTPException(status_code=404, detail="YouTube account not found")
    start = youtube_connect_start(session)
    return {"ok": True, "account_id": account_id, **start}


@router.post("/{account_id}/disconnect")
def disconnect(account_id: str, session: Session = Depends(get_session)):
    account = session.get(PlatformAccount, account_id)
    if not account:
        raise HTTPException(404, "Account not found")
    if account.provider == Provider.youtube:
        return youtube_disconnect(account_id, session)
    account.status = "revoked"; account.encrypted_access_token = None; account.encrypted_refresh_token = None
    session.add(account); session.commit()
    return {"ok": True, "account": _safe_account(account)}


@router.patch("/{account_id}/default")
def set_default(account_id: str, session: Session = Depends(get_session)):
    account = session.get(PlatformAccount, account_id)
    if not account:
        raise HTTPException(404, "Account not found")
    for other in session.exec(select(PlatformAccount).where(PlatformAccount.provider == account.provider)).all():
        other.is_default = False; session.add(other)
    account.is_default = True; account.updated_at = _now(); session.add(account); session.commit()
    return {"ok": True, "account": _safe_account(account)}


@router.post("/youtube/test/{account_id}")
def test_youtube_account(account_id: str, session: Session = Depends(get_session)):
    account = session.get(PlatformAccount, account_id)
    if not account or account.provider != Provider.youtube:
        raise HTTPException(status_code=404, detail="YouTube account not found")
    if account.status != "connected":
        return {"ok": False, "status": account.status, "last_error": account.last_error}
    account.last_successful_api_call = _now()
    account.updated_at = _now()
    session.add(account); session.commit()
    return {"ok": True, "status": account.status, "dry_run_safe": True, "account": _safe_account(account)}
