"""
Sheets Results API - Endpoints for Add-on to poll scheduled job results
"""

from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel
import json
import logging

from ...auth_core import get_current_identity
from ...api.progress import get_redis

router = APIRouter(prefix="/sheets", tags=["Sheets Integration"])
log = logging.getLogger(__name__)


class AcknowledgeRequest(BaseModel):
    storage_key: str


@router.get("/scheduled-results")
@router.post("/scheduled-results")
async def get_scheduled_results(
    sheet_id: str = Query(..., description="Google Sheets spreadsheet ID"),
    identity: dict = Depends(get_current_identity),
):
    """
    Get pending scheduled results for a spreadsheet.

    The Add-on polls this endpoint periodically to check for new scheduled data.

    Returns:
        List of pending results with headers, rows, and metadata

    Authentication: Requires Clerk Bearer token (user's JWT from login)
    """
    account_id = identity.get("org_id")  # Use org_id from Clerk token
    if not account_id:
        raise HTTPException(401, "Missing org_id in token")

    try:
        redis_conn = await get_redis()
        if not redis_conn:
            return {"results": []}

        # Get list of pending results for this sheet (scoped by account_id to prevent cross-tenant access)
        list_key = f"scheduled_results:{account_id}:{sheet_id}"
        storage_keys = await redis_conn.lrange(list_key, 0, -1)

        if not storage_keys:
            return {"results": []}

        results = []
        expected_prefix = f"scheduled_result:{account_id}:{sheet_id}:"
        for key_bytes in storage_keys:
            try:
                key = key_bytes.decode() if isinstance(key_bytes, bytes) else key_bytes
                if not str(key).startswith(expected_prefix):
                    # Poisoned or legacy key (unscoped); remove it to avoid leaks.
                    await redis_conn.lrem(list_key, 0, key)
                    continue

                # Get the result data
                value_bytes = await redis_conn.get(key)
                if not value_bytes:
                    # Result expired or already processed, remove from list
                    await redis_conn.lrem(list_key, 0, key)
                    continue

                value = (
                    value_bytes.decode()
                    if isinstance(value_bytes, bytes)
                    else value_bytes
                )
                result_data = json.loads(value)

                # Only return if status is pending
                if result_data.get("status") == "pending_write":
                    results.append(
                        {
                            "storage_key": key,
                            "sheet_name": result_data["sheet_name"],
                            "headers": result_data["headers"],
                            "rows": result_data["rows"],
                            "metadata": result_data.get("metadata", {}),
                            "created_at": result_data.get("created_at"),
                        }
                    )

            except Exception as e:
                log.error(f"Error processing scheduled result {key}: {e}")
                continue

        return {"results": results}

    except Exception as e:
        log.error(f"Error fetching scheduled results: {e}")
        raise HTTPException(500, f"Failed to fetch results: {str(e)}")


@router.post("/scheduled-results/ack")
async def acknowledge_result(
    request: AcknowledgeRequest, identity: dict = Depends(get_current_identity)
):
    """
    Acknowledge that a scheduled result was successfully written to Sheets.

    This marks the result as processed and prevents it from being returned again.

    Authentication: Requires Clerk Bearer token (user's JWT from login)
    """
    account_id = identity.get("org_id")  # Use org_id from Clerk token
    if not account_id:
        raise HTTPException(401, "Missing org_id in token")

    try:
        redis_conn = await get_redis()
        if not redis_conn:
            return {"ok": True, "message": "Redis not available"}

        storage_key = request.storage_key
        if not storage_key:
            raise HTTPException(400, "storage_key is required")

        # Validate ownership from the key itself. Expected format:
        # scheduled_result:{account_id}:{sheet_id}:{sheet_name}[...]
        parts = storage_key.split(":", 3)
        if len(parts) < 3 or parts[0] != "scheduled_result":
            raise HTTPException(400, "Invalid storage_key format")
        key_account_id = parts[1]
        key_sheet_id = parts[2]
        if key_account_id != account_id:
            # Hide existence on mismatch
            raise HTTPException(404, "Result not found")

        # Update status to 'written'
        value_bytes = await redis_conn.get(storage_key)
        if value_bytes:
            value = (
                value_bytes.decode() if isinstance(value_bytes, bytes) else value_bytes
            )
            result_data = json.loads(value)
            result_data["status"] = "written"
            result_data["written_at"] = datetime.utcnow().isoformat()

            # Update with shorter TTL (keep for audit for 1 hour)
            await redis_conn.setex(storage_key, 3600, json.dumps(result_data))

        # Remove from pending list
        list_key = f"scheduled_results:{account_id}:{key_sheet_id}"
        await redis_conn.lrem(list_key, 0, storage_key)

        log.info(f"Acknowledged scheduled result: {storage_key}")

        return {"ok": True, "message": "Result acknowledged"}

    except HTTPException:
        raise
    except Exception as e:
        log.error(f"Error acknowledging result: {e}")
        raise HTTPException(500, f"Failed to acknowledge: {str(e)}")


# Import datetime
from datetime import datetime


@router.get("/alerts")
async def get_user_alerts(
    sheet_id: str = Query(..., description="Google Sheets spreadsheet ID"),
    identity: dict = Depends(get_current_identity),
):
    """
    Get active alerts for the user (auth failures, paused schedules, etc.)

    The Add-on can check this endpoint and display banners/notifications in the UI.

    Authentication: Requires Clerk Bearer token (user's JWT from login)
    """
    account_id = identity.get("org_id")  # Use org_id from Clerk token
    if not account_id:
        raise HTTPException(401, "Missing org_id in token")

    try:
        from ...db import get_session
        from ...models import (
            Integration,
            IntegrationProvider,
            IntegrationStatus,
            ScheduledJob,
        )

        alerts = []

        async for db in get_session():
            # Pull all schedules for this sheet so alerting can distinguish:
            # no schedules vs paused schedules vs auth-paused schedules.
            from sqlalchemy import select as sa_select

            jobs_result = await db.execute(
                sa_select(ScheduledJob).where(
                    ScheduledJob.account_id == str(account_id),
                    ScheduledJob.sheet_id == sheet_id,
                )
            )
            sheet_jobs = jobs_result.scalars().all()
            paused_jobs = [job for job in sheet_jobs if job.paused_by_system]
            has_schedules = bool(sheet_jobs)
            has_salesforce_auth_paused_job = any(
                isinstance(job.pause_reason, str)
                and "salesforce" in job.pause_reason.lower()
                and any(
                    marker in job.pause_reason.lower()
                    for marker in ["expired", "credential", "reconnect", "auth"]
                )
                for job in paused_jobs
            )

            sf_result = await db.execute(
                sa_select(Integration).where(
                    Integration.account_id == str(account_id),
                    Integration.provider == IntegrationProvider.SALESFORCE,
                )
            )
            sf_integration = sf_result.scalar_one_or_none()
            has_refresh_token = bool(
                sf_integration
                and sf_integration.refresh_token
                and str(sf_integration.refresh_token).strip()
            )

            # Only show this banner when there are schedules to be affected.
            # Otherwise "scheduled automations are paused" is misleading noise.
            needs_reconnect = bool(has_salesforce_auth_paused_job)
            if has_schedules:
                if not sf_integration:
                    needs_reconnect = True
                elif sf_integration.status != IntegrationStatus.CONNECTED:
                    needs_reconnect = True
                elif (
                    sf_integration.expires_at
                    and sf_integration.expires_at < datetime.utcnow()
                    and not has_refresh_token
                ):
                    # Expired access token alone is not fatal if refresh_token exists.
                    # Most connected Salesforce integrations will auto-refresh on use.
                    needs_reconnect = True

            log.debug(
                "[alerts] account=%s sheet=%s schedules=%s paused=%s sf_status=%s has_refresh=%s needs_reconnect=%s",
                account_id,
                sheet_id,
                len(sheet_jobs),
                len(paused_jobs),
                str(sf_integration.status) if sf_integration else "missing",
                has_refresh_token,
                needs_reconnect,
            )

            if needs_reconnect:
                alerts.append(
                    {
                        "type": "auth_failure",
                        "severity": "error",
                        "provider": "salesforce",
                        "message": "Your Salesforce connection has expired. Scheduled automations are paused.",
                        "action_label": "Reconnect",
                        "action_type": "reconnect_salesforce",
                    }
                )

            for job in paused_jobs:
                schedule_name = (
                    job.config.get("name", job.job_type)
                    if isinstance(job.config, dict)
                    else job.job_type
                )

                alerts.append(
                    {
                        "type": "schedule_paused",
                        "severity": "warning",
                        "job_id": job.id,
                        "message": f'Schedule "{schedule_name}" is paused: {job.pause_reason}',
                        "action_label": "View Schedule",
                        "action_type": "open_schedule_manager",
                    }
                )

        return {"alerts": alerts}

    except Exception as e:
        log.error(f"Error fetching alerts: {e}")
        raise HTTPException(500, f"Failed to fetch alerts: {str(e)}")
