"""Forecast Operating Sheet endpoints."""

from __future__ import annotations

import hashlib
import json
import logging
from datetime import datetime
from typing import Any, Dict, List, Optional
from uuid import uuid4

from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, status
from pydantic import BaseModel, Field

from ...auth_security import require_account
from ...services.forecast_canonicalizer import ForecastCanonicalizer
from ...services.forecast_recon_service import ForecastReconService
from ...services.forecast_soql_builder import ForecastSoqlBuilder
from ...services.salesforce_gateway import SalesforceGateway, get_salesforce_gateway
from ...workers.forecast_pull_worker import run_forecast_pull_job

logger = logging.getLogger(__name__)

router = APIRouter()


class ForecastReconRequest(BaseModel):
    integration_id: Optional[str] = None


class ForecastReconResponse(BaseModel):
    status: str
    recommendations: Dict[str, Any]
    confidence_scores: Dict[str, Any]
    sample_preview: Dict[str, Any]
    detected_fields: List[Dict[str, Any]] = Field(default_factory=list)
    record_types: List[Dict[str, Any]] = Field(default_factory=list)
    forecast_categories: List[str] = Field(default_factory=list)
    custom_fields: List[str] = Field(default_factory=list)


class ForecastSetupRequest(BaseModel):
    spreadsheet_id: str
    integration_id: Optional[str] = None
    config: Dict[str, Any] = Field(default_factory=dict)
    mappings: List[Dict[str, Any]] = Field(default_factory=list)


class ForecastSetupResponse(BaseModel):
    status: str
    config: Dict[str, Any]
    mappings: List[Dict[str, Any]]
    config_hash: str
    sheets_created: List[str] = Field(default_factory=list)
    named_ranges_created: List[str] = Field(default_factory=list)


class ForecastPullRequest(BaseModel):
    spreadsheet_id: str
    integration_id: Optional[str] = None
    config_hash: Optional[str] = None
    config: Dict[str, Any] = Field(default_factory=dict)
    mappings: List[Dict[str, Any]] = Field(default_factory=list)
    full_refresh: bool = False


class ForecastPullResponse(BaseModel):
    status: str
    rows_pulled: Optional[int] = None
    canonical_rows: Optional[List[Dict[str, Any]]] = None
    job_id: Optional[str] = None
    query: Optional[str] = None


def _compute_hash(config: Dict[str, Any], mappings: List[Dict[str, Any]]) -> str:
    payload = json.dumps({"config": config, "mappings": mappings}, sort_keys=True, default=str)
    return "sha256:" + hashlib.sha256(payload.encode("utf-8")).hexdigest()


@router.post("/forecast/recon", response_model=ForecastReconResponse)
async def run_forecast_recon(
    body: ForecastReconRequest,
    gateway: SalesforceGateway = Depends(get_salesforce_gateway),
    account_id: str = Depends(require_account),
) -> Dict[str, Any]:
    """Run pre-recon against Salesforce to generate interview defaults."""
    _ = account_id  # Reserved for future audit logging
    service = ForecastReconService(gateway)
    result = await service.run_recon(body.integration_id or "")
    return result


@router.post("/forecast/setup", response_model=ForecastSetupResponse)
async def save_forecast_setup(
    body: ForecastSetupRequest,
    account_id: str = Depends(require_account),
) -> Dict[str, Any]:
    """
    Persist config hash and echo back normalized config/mappings.

    Storage is handled client-side (Apps Script writes to _FM_* sheets). The
    backend provides deterministic hashing + defaults.
    """
    _ = account_id  # for symmetry with other endpoints
    config = dict(body.config or {})
    config.setdefault("template_type", "forecast_v1")
    config.setdefault("template_version", "1.0.0")
    config.setdefault("sfdc_integration_id", body.integration_id)
    config.setdefault("setup_completed_at", datetime.utcnow().isoformat())
    config.setdefault("forecast_category_source", config.get("forecast_category_source", "ForecastCategoryName"))
    config.setdefault("revenue_field", config.get("revenue_field", "Amount"))
    config.setdefault("credit_owner_field", config.get("credit_owner_field", "OwnerId"))
    config.setdefault("fiscal_year_start_month", config.get("fiscal_year_start_month", 1))
    config_hash = _compute_hash(config, body.mappings)
    config["config_hash"] = config_hash
    config["config_version"] = int(config.get("config_version") or 1)

    return {
        "status": "ok",
        "config": config,
        "mappings": body.mappings or [],
        "config_hash": config_hash,
        "sheets_created": [
            "_FM_Config",
            "_FM_ConfigHistory",
            "_FM_Mappings",
            "_FM_Data_Opportunities",
            "_FM_Metrics",
            "_FM_Snapshots",
            "_FM_Movements",
        ],
        "named_ranges_created": [
            "FM_Config",
            "FM_Mappings",
            "FM_Opportunities",
            "FM_Snapshots",
            "FM_Movements",
        ],
    }


async def _query_with_pagination(gateway: SalesforceGateway, soql: str) -> List[Dict[str, Any]]:
    """Follow nextRecordsUrl pagination without OFFSET."""
    records: List[Dict[str, Any]] = []
    result = await gateway.soql(soql)
    if isinstance(result, dict):
        records.extend(result.get("records", []))
        while result.get("nextRecordsUrl"):
            result = await gateway.fetch_next(result["nextRecordsUrl"])
            if not isinstance(result, dict):
                break
            records.extend(result.get("records", []))
            if len(records) > 50000:
                logger.warning("Stopping pagination after 50k rows for safety")
                break
    return records


@router.post("/forecast/pull", response_model=ForecastPullResponse)
async def pull_forecast_data(
    body: ForecastPullRequest,
    background_tasks: BackgroundTasks,
    gateway: SalesforceGateway = Depends(get_salesforce_gateway),
    account_id: str = Depends(require_account),
) -> Dict[str, Any]:
    """Pull opportunities, canonicalize, and return to Apps Script."""

    config = dict(body.config or {})
    mappings = body.mappings or []
    config.setdefault("forecast_category_source", "ForecastCategoryName")
    config.setdefault("revenue_field", config.get("revenue_field", "Amount"))

    # Optionally allow background pull (heuristic flag)
    if body.full_refresh and config.get("force_background_pull"):
        job_id = str(uuid4())
        background_tasks.add_task(
            run_forecast_pull_job,
            job_id,
            account_id,
            body.integration_id,
            config,
            mappings,
        )
        return {"status": "queued", "job_id": job_id}

    builder = ForecastSoqlBuilder()
    canonicalizer = ForecastCanonicalizer(config, mappings)

    try:
        soql = builder.build_opportunity_query(config)
        records = await _query_with_pagination(gateway, soql)
    except Exception as exc:  # noqa: BLE001
        logger.warning("Forecast pull failed: %s", exc)
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Forecast pull failed. Please verify your configuration and try again.",
        ) from exc

    canonical_rows = [canonicalizer.canonicalize(rec) for rec in records]
    return {
        "status": "completed",
        "rows_pulled": len(records),
        "canonical_rows": canonical_rows,
        "query": soql,
    }
