"""
Scheduling API - CRUD operations for scheduled jobs
"""

from fastapi import APIRouter, Depends, HTTPException
from typing import Dict, Any, Optional
from pydantic import BaseModel
from datetime import datetime
import json
from .deps import require_api_key

router = APIRouter(prefix="/salesforce/schedules", tags=["Scheduling"])


class CreateScheduleRequest(BaseModel):
    name: str
    job_type: str  # 'soql_query' or 'report_export'
    config: Dict[str, Any]  # {soql, target_sheet} or {report_id, target_sheet}
    schedule: str  # 'daily', 'hourly', or cron expression
    sheet_id: str
    sheet_gid: Optional[str] = None


@router.post("")
async def create_schedule(
    request: CreateScheduleRequest, api_ctx=Depends(require_api_key)
):
    """Create a new scheduled job."""
    from ...db import get_session
    from ...models import ScheduledJob
    from ...middleware.quota_checker import get_quota
    from ...services.google_sheets_oauth import ensure_google_access_token
    from croniter import croniter
    import uuid

    account_id = api_ctx.account_id
    if not account_id:
        raise HTTPException(401, "Missing account_id")

    async for db in get_session():
        try:
            access_token, _integration = await ensure_google_access_token(
                str(account_id), db
            )
            if not access_token:
                raise HTTPException(
                    403,
                    "Google Sheets connection required to create schedules. Connect Google in Schedule Manager first.",
                )

            # Check quota limits
            quota = await get_quota(str(account_id), db)
            limits = quota.get_limits()

            # Count active schedules
            from sqlalchemy import select, func

            result = await db.execute(
                select(func.count(ScheduledJob.id)).where(
                    ScheduledJob.account_id == str(account_id),
                    ScheduledJob.is_active == True,
                )
            )
            active_count = result.scalar() or 0

            if active_count >= limits["max_schedules"]:
                raise HTTPException(
                    429,
                    f"Schedule limit reached ({active_count}/{limits['max_schedules']}). Upgrade to Pro for more schedules.",
                )

            # Validate schedule format
            if request.schedule not in ["daily", "hourly"]:
                # Validate cron if not preset
                try:
                    croniter(request.schedule)
                except Exception:
                    raise HTTPException(
                        400,
                        "Invalid schedule format. Use 'daily', 'hourly', or valid cron expression.",
                    )

            # Check if frequency is allowed for tier
            if (
                request.schedule == "hourly"
                and "hourly" not in limits["allowed_frequencies"]
            ):
                raise HTTPException(403, "Hourly schedules require Pro tier")

            # Calculate next run time
            from ...services.scheduler_service import SchedulerService

            next_run = SchedulerService()._calculate_next_run(
                request.schedule, datetime.utcnow()
            )

            # Create scheduled job
            config = request.config or {}
            sheet_gid = (
                request.sheet_gid
                or config.get("sheet_gid")
                or config.get("sheetGid")
            )
            cached_title = config.get("cached_sheet_title")
            if not cached_title:
                cached_title = config.get("target_sheet") or config.get("sheet_name")
            if cached_title:
                config["cached_sheet_title"] = cached_title
            job = ScheduledJob(
                id=f"job_{uuid.uuid4().hex[:12]}",
                account_id=str(account_id),
                sheet_id=request.sheet_id,
                sheet_gid=str(sheet_gid) if sheet_gid is not None else None,
                job_type=request.job_type,
                config=config,
                schedule=request.schedule,
                next_run_at=next_run,
                is_active=True,
                created_at=datetime.utcnow(),
                updated_at=datetime.utcnow(),
            )

            db.add(job)
            await db.commit()

            return {
                "ok": True,
                "data": {
                    "id": job.id,
                    "name": request.name,
                    "next_run_at": next_run.isoformat(),
                },
            }

        except HTTPException:
            raise
        except Exception as e:
            raise HTTPException(500, "Failed to create schedule")


@router.get("")
async def list_schedules(
    sheet_id: Optional[str] = None,
    api_ctx=Depends(require_api_key),
):
    """List scheduled jobs for current user (optionally filtered by sheet_id)."""
    from ...db import get_session
    from ...models import ScheduledJob
    from sqlalchemy import select

    account_id = api_ctx.account_id
    if not account_id:
        raise HTTPException(401, "Missing account_id")

    async for db in get_session():
        try:
            query = select(ScheduledJob).where(
                ScheduledJob.account_id == str(account_id)
            )
            if sheet_id:
                query = query.where(ScheduledJob.sheet_id == sheet_id)
            query = query.order_by(ScheduledJob.created_at.desc())
            result = await db.execute(query)

            jobs = result.scalars().all()

            return {
                "ok": True,
                "data": {
                    "schedules": [
                        {
                            "id": job.id,
                            "name": job.config.get("name")
                            if isinstance(job.config, dict)
                            else job.job_type,
                            "sheet_id": job.sheet_id,
                            "sheet_gid": job.sheet_gid,
                            "job_type": job.job_type,
                            "config": job.config,
                            "schedule": job.schedule,
                            "next_run_at": job.next_run_at.isoformat()
                            if job.next_run_at
                            else None,
                            "last_run_at": job.last_run_at.isoformat()
                            if job.last_run_at
                            else None,
                            "last_write_status": job.last_write_status,
                            "last_write_at": job.last_write_at.isoformat()
                            if job.last_write_at
                            else None,
                            "last_write_error": job.last_write_error,
                            "is_active": job.is_active,
                            "paused_by_system": job.paused_by_system,
                            "pause_reason": job.pause_reason,
                            "error_count": job.error_count,
                        }
                        for job in jobs
                    ]
                },
            }

        except Exception as e:
            raise HTTPException(500, "Failed to list schedules")


@router.post("/{schedule_id}/pause")
async def pause_schedule(schedule_id: str, api_ctx=Depends(require_api_key)):
    """Pause a scheduled job."""
    from ...db import get_session
    from ...models import ScheduledJob
    from sqlalchemy import select

    account_id = api_ctx.account_id
    if not account_id:
        raise HTTPException(401, "Missing account_id")

    async for db in get_session():
        try:
            result = await db.execute(
                select(ScheduledJob).where(
                    ScheduledJob.id == schedule_id,
                    ScheduledJob.account_id == str(account_id),
                )
            )

            job = result.scalar_one_or_none()
            if not job:
                raise HTTPException(404, "Schedule not found")

            job.is_active = False
            job.updated_at = datetime.utcnow()
            await db.commit()

            return {"ok": True}

        except HTTPException:
            raise
        except Exception as e:
            raise HTTPException(500, "Failed to pause schedule")


@router.post("/{schedule_id}/resume")
async def resume_schedule(schedule_id: str, api_ctx=Depends(require_api_key)):
    """Resume a paused scheduled job."""
    from ...db import get_session
    from ...models import ScheduledJob
    from sqlalchemy import select

    account_id = api_ctx.account_id
    if not account_id:
        raise HTTPException(401, "Missing account_id")

    async for db in get_session():
        try:
            result = await db.execute(
                select(ScheduledJob).where(
                    ScheduledJob.id == schedule_id,
                    ScheduledJob.account_id == str(account_id),
                )
            )

            job = result.scalar_one_or_none()
            if not job:
                raise HTTPException(404, "Schedule not found")

            job.is_active = True
            job.paused_by_system = False
            job.pause_reason = None
            job.updated_at = datetime.utcnow()
            await db.commit()

            return {"ok": True}

        except HTTPException:
            raise
        except Exception as e:
            raise HTTPException(500, "Failed to resume schedule")


@router.post("/{schedule_id}/run")
async def run_schedule_now(schedule_id: str, api_ctx=Depends(require_api_key)):
    """Run a scheduled job immediately."""
    from ...services.scheduler_service import get_scheduler
    from ...db import get_session
    from ...models import ScheduledJob
    from sqlalchemy import select

    account_id = api_ctx.account_id
    if not account_id:
        raise HTTPException(401, "Missing account_id")

    async for db in get_session():
        try:
            result = await db.execute(
                select(ScheduledJob).where(
                    ScheduledJob.id == schedule_id,
                    ScheduledJob.account_id == str(account_id),
                )
            )

            job = result.scalar_one_or_none()
            if not job:
                raise HTTPException(404, "Schedule not found")

            # Execute job immediately
            scheduler = await get_scheduler()
            await scheduler._execute_job(job, db)

            return {"ok": True, "message": "Job executed successfully"}

        except HTTPException:
            raise
        except Exception as e:
            raise HTTPException(500, "Failed to run schedule")


@router.post("/{schedule_id}/dry-run")
async def dry_run_schedule(
    schedule_id: str, limit: int = 50, api_ctx=Depends(require_api_key)
):
    """Execute a schedule once without writing to Sheets."""
    from ...db import get_session
    from ...models import ScheduledJob
    from ...services.salesforce_gateway import SalesforceGateway
    from ...middleware.quota_checker import get_quota
    from sqlalchemy import select

    account_id = api_ctx.account_id
    if not account_id:
        raise HTTPException(401, "Missing account_id")

    limit = max(1, min(limit, 200))

    async for db in get_session():
        result = await db.execute(
            select(ScheduledJob).where(
                ScheduledJob.id == schedule_id,
                ScheduledJob.account_id == str(account_id),
            )
        )

        job = result.scalar_one_or_none()
        if not job:
            raise HTTPException(404, "Schedule not found")

        config = (
            json.loads(job.config)
            if isinstance(job.config, str)
            else (job.config or {})
        )

        if job.job_type != "soql_query":
            raise HTTPException(501, "Dry run currently supports SOQL schedules only")

        soql = config.get("soql")
        if not soql:
            raise HTTPException(400, "Schedule is missing a SOQL query")

        gateway = await SalesforceGateway.for_tenant(job.account_id, db)
        quota = await get_quota(job.account_id, db)
        max_rows = quota.get_limits()["rows_per_run"]
        preview_limit = min(limit, max_rows)

        records = []
        truncated = False
        async for batch in gateway.paginate_soql(soql):
            records.extend(batch)
            if len(records) >= preview_limit:
                records = records[:preview_limit]
                truncated = True
                break

        headers = list(records[0].keys()) if records else []
        rows = [[record.get(h, "") for h in headers] for record in records]

        return {
            "ok": True,
            "data": {
                "headers": headers,
                "rows": rows,
                "row_count": len(rows),
                "limit": preview_limit,
                "more_available": truncated,
                "soql": soql,
            },
        }


@router.post("/pause-all")
async def pause_all_schedules(api_ctx=Depends(require_api_key)):
    """Pause all active schedules for current user."""
    from ...db import get_session
    from ...models import ScheduledJob
    from sqlalchemy import update

    account_id = api_ctx.account_id
    if not account_id:
        raise HTTPException(401, "Missing account_id")

    async for db in get_session():
        stmt = (
            update(ScheduledJob)
            .where(
                ScheduledJob.account_id == str(account_id),
                ScheduledJob.is_active == True,
                ScheduledJob.paused_by_system == False,
            )
            .values(
                is_active=False,
                pause_reason="Paused by user",
                updated_at=datetime.utcnow(),
            )
        )
        result = await db.execute(stmt)
        await db.commit()

        return {"ok": True, "paused": result.rowcount if result else 0}


@router.post("/resume-all")
async def resume_all_schedules(api_ctx=Depends(require_api_key)):
    """Resume all user-paused schedules."""
    from ...db import get_session
    from ...models import ScheduledJob
    from sqlalchemy import update

    account_id = api_ctx.account_id
    if not account_id:
        raise HTTPException(401, "Missing account_id")

    async for db in get_session():
        stmt = (
            update(ScheduledJob)
            .where(
                ScheduledJob.account_id == str(account_id),
                ScheduledJob.is_active == False,
                ScheduledJob.paused_by_system == False,
            )
            .values(is_active=True, pause_reason=None, updated_at=datetime.utcnow())
        )
        result = await db.execute(stmt)
        await db.commit()

        return {"ok": True, "resumed": result.rowcount if result else 0}


@router.delete("/{schedule_id}")
async def delete_schedule(schedule_id: str, api_ctx=Depends(require_api_key)):
    """Delete a scheduled job."""
    from ...db import get_session
    from ...models import ScheduledJob
    from sqlalchemy import select

    account_id = api_ctx.account_id
    if not account_id:
        raise HTTPException(401, "Missing account_id")

    async for db in get_session():
        try:
            result = await db.execute(
                select(ScheduledJob).where(
                    ScheduledJob.id == schedule_id,
                    ScheduledJob.account_id == str(account_id),
                )
            )

            job = result.scalar_one_or_none()
            if not job:
                raise HTTPException(404, "Schedule not found")

            await db.delete(job)
            await db.commit()

            return {"ok": True}

        except HTTPException:
            raise
        except Exception as e:
            raise HTTPException(500, "Failed to delete schedule")


@router.get("/{schedule_id}/history")
async def get_schedule_history(
    schedule_id: str, limit: int = 50, api_ctx=Depends(require_api_key)
):
    """Get execution history for a scheduled job."""
    from ...db import get_session
    from ...models import ScheduledJob, JobRun
    from sqlalchemy import select

    account_id = api_ctx.account_id
    if not account_id:
        raise HTTPException(401, "Missing account_id")

    async for db in get_session():
        try:
            # Verify job belongs to account
            result = await db.execute(
                select(ScheduledJob).where(
                    ScheduledJob.id == schedule_id,
                    ScheduledJob.account_id == str(account_id),
                )
            )

            job = result.scalar_one_or_none()
            if not job:
                raise HTTPException(404, "Schedule not found")

            # Get run history
            runs_result = await db.execute(
                select(JobRun)
                .where(JobRun.job_id == schedule_id)
                .order_by(JobRun.started_at.desc())
                .limit(limit)
            )

            runs = runs_result.scalars().all()

            return {
                "ok": True,
                "data": {
                    "schedule_id": schedule_id,
                    "schedule_name": job.config.get("name")
                    if isinstance(job.config, dict)
                    else job.job_type,
                    "runs": [
                        {
                            "id": run.id,
                            "status": run.status,
                            "started_at": run.started_at.isoformat()
                            if run.started_at
                            else None,
                            "completed_at": run.completed_at.isoformat()
                            if run.completed_at
                            else None,
                            "duration_ms": run.duration_ms,
                            "rows_returned": run.rows_returned,
                            "error_message": run.error_message,
                            "output_location": run.output_location,
                        }
                        for run in runs
                    ],
                },
            }

        except HTTPException:
            raise
        except Exception as e:
            raise HTTPException(500, "Failed to get history")
