"""
Recurring Schedules Router

API endpoints for managing recurring monitoring schedules.
"""

from fastapi import APIRouter, Request, Depends, HTTPException, Query
from fastapi.responses import HTMLResponse, JSONResponse
from sqlalchemy.orm import Session
from typing import Optional
from datetime import datetime
import json

from backend.database import get_db
from backend.models import RecurringSchedule, MonitoringLocation, Project, RosterUnit
from backend.services.recurring_schedule_service import get_recurring_schedule_service
from backend.templates_config import templates

router = APIRouter(prefix="/api/projects/{project_id}/recurring-schedules", tags=["recurring-schedules"])


# ============================================================================
# List and Get
# ============================================================================

@router.get("/")
async def list_recurring_schedules(
    project_id: str,
    db: Session = Depends(get_db),
    enabled_only: bool = Query(False),
):
    """
    List all recurring schedules for a project.
    """
    project = db.query(Project).filter_by(id=project_id).first()
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    query = db.query(RecurringSchedule).filter_by(project_id=project_id)
    if enabled_only:
        query = query.filter_by(enabled=True)

    schedules = query.order_by(RecurringSchedule.created_at.desc()).all()

    return {
        "schedules": [
            {
                "id": s.id,
                "name": s.name,
                "schedule_type": s.schedule_type,
                "device_type": s.device_type,
                "location_id": s.location_id,
                "unit_id": s.unit_id,
                "enabled": s.enabled,
                "weekly_pattern": json.loads(s.weekly_pattern) if s.weekly_pattern else None,
                "interval_type": s.interval_type,
                "cycle_time": s.cycle_time,
                "include_download": s.include_download,
                "timezone": s.timezone,
                "next_occurrence": s.next_occurrence.isoformat() if s.next_occurrence else None,
                "last_generated_at": s.last_generated_at.isoformat() if s.last_generated_at else None,
                "created_at": s.created_at.isoformat() if s.created_at else None,
            }
            for s in schedules
        ],
        "count": len(schedules),
    }


@router.get("/{schedule_id}")
async def get_recurring_schedule(
    project_id: str,
    schedule_id: str,
    db: Session = Depends(get_db),
):
    """
    Get a specific recurring schedule.
    """
    schedule = db.query(RecurringSchedule).filter_by(
        id=schedule_id,
        project_id=project_id,
    ).first()

    if not schedule:
        raise HTTPException(status_code=404, detail="Schedule not found")

    # Get related location and unit info
    location = db.query(MonitoringLocation).filter_by(id=schedule.location_id).first()
    unit = None
    if schedule.unit_id:
        unit = db.query(RosterUnit).filter_by(id=schedule.unit_id).first()

    return {
        "id": schedule.id,
        "name": schedule.name,
        "schedule_type": schedule.schedule_type,
        "device_type": schedule.device_type,
        "location_id": schedule.location_id,
        "location_name": location.name if location else None,
        "unit_id": schedule.unit_id,
        "unit_name": unit.id if unit else None,
        "enabled": schedule.enabled,
        "weekly_pattern": json.loads(schedule.weekly_pattern) if schedule.weekly_pattern else None,
        "interval_type": schedule.interval_type,
        "cycle_time": schedule.cycle_time,
        "include_download": schedule.include_download,
        "timezone": schedule.timezone,
        "next_occurrence": schedule.next_occurrence.isoformat() if schedule.next_occurrence else None,
        "last_generated_at": schedule.last_generated_at.isoformat() if schedule.last_generated_at else None,
        "created_at": schedule.created_at.isoformat() if schedule.created_at else None,
        "updated_at": schedule.updated_at.isoformat() if schedule.updated_at else None,
    }


# ============================================================================
# Create
# ============================================================================

@router.post("/")
async def create_recurring_schedule(
    project_id: str,
    request: Request,
    db: Session = Depends(get_db),
):
    """
    Create recurring schedules for one or more locations.

    Body for weekly_calendar (supports multiple locations):
    {
        "name": "Weeknight Monitoring",
        "schedule_type": "weekly_calendar",
        "location_ids": ["uuid1", "uuid2"],  // Array of location IDs
        "weekly_pattern": {
            "monday": {"enabled": true, "start": "19:00", "end": "07:00"},
            "tuesday": {"enabled": false},
            ...
        },
        "include_download": true,
        "auto_increment_index": true,
        "timezone": "America/New_York"
    }

    Body for simple_interval (supports multiple locations):
    {
        "name": "24/7 Continuous",
        "schedule_type": "simple_interval",
        "location_ids": ["uuid1", "uuid2"],  // Array of location IDs
        "interval_type": "daily",
        "cycle_time": "00:00",
        "include_download": true,
        "auto_increment_index": true,
        "timezone": "America/New_York"
    }

    Legacy single location support (backwards compatible):
    {
        "name": "...",
        "location_id": "uuid",  // Single location ID
        ...
    }
    """
    project = db.query(Project).filter_by(id=project_id).first()
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    data = await request.json()

    # Support both location_ids (array) and location_id (single) for backwards compatibility
    location_ids = data.get("location_ids", [])
    if not location_ids and data.get("location_id"):
        location_ids = [data.get("location_id")]

    if not location_ids:
        raise HTTPException(status_code=400, detail="At least one location is required")

    # Validate all locations exist
    locations = db.query(MonitoringLocation).filter(
        MonitoringLocation.id.in_(location_ids),
        MonitoringLocation.project_id == project_id,
    ).all()

    if len(locations) != len(location_ids):
        raise HTTPException(status_code=404, detail="One or more locations not found")

    service = get_recurring_schedule_service(db)
    created_schedules = []
    base_name = data.get("name", "Unnamed Schedule")

    # Parse one-off datetime fields if applicable
    one_off_start = None
    one_off_end = None
    if data.get("schedule_type") == "one_off":
        from zoneinfo import ZoneInfo

        tz = ZoneInfo(data.get("timezone", "America/New_York"))

        start_dt_str = data.get("start_datetime")
        end_dt_str = data.get("end_datetime")

        if not start_dt_str or not end_dt_str:
            raise HTTPException(status_code=400, detail="One-off schedules require start and end date/time")

        try:
            start_local = datetime.fromisoformat(start_dt_str).replace(tzinfo=tz)
            end_local = datetime.fromisoformat(end_dt_str).replace(tzinfo=tz)
        except ValueError:
            raise HTTPException(status_code=400, detail="Invalid datetime format")

        duration = end_local - start_local
        if duration.total_seconds() < 900:
            raise HTTPException(status_code=400, detail="Duration must be at least 15 minutes")
        if duration.total_seconds() > 86400:
            raise HTTPException(status_code=400, detail="Duration cannot exceed 24 hours")

        from datetime import timezone as dt_timezone
        now_local = datetime.now(tz)
        if start_local <= now_local:
            raise HTTPException(status_code=400, detail="Start time must be in the future")

        # Convert to UTC for storage
        one_off_start = start_local.astimezone(ZoneInfo("UTC")).replace(tzinfo=None)
        one_off_end = end_local.astimezone(ZoneInfo("UTC")).replace(tzinfo=None)

    # Create a schedule for each location
    for location in locations:
        # Determine device type from location
        device_type = "slm" if location.location_type == "sound" else "seismograph"

        # Append location name if multiple locations
        schedule_name = f"{base_name} - {location.name}" if len(locations) > 1 else base_name

        schedule = service.create_schedule(
            project_id=project_id,
            location_id=location.id,
            name=schedule_name,
            schedule_type=data.get("schedule_type", "weekly_calendar"),
            device_type=device_type,
            unit_id=data.get("unit_id"),
            weekly_pattern=data.get("weekly_pattern"),
            interval_type=data.get("interval_type"),
            cycle_time=data.get("cycle_time"),
            include_download=data.get("include_download", True),
            auto_increment_index=data.get("auto_increment_index", True),
            timezone=data.get("timezone", "America/New_York"),
            start_datetime=one_off_start,
            end_datetime=one_off_end,
        )

        # Generate actions immediately so they appear right away
        generated_actions = service.generate_actions_for_schedule(schedule, horizon_days=7)

        created_schedules.append({
            "schedule_id": schedule.id,
            "location_id": location.id,
            "location_name": location.name,
            "actions_generated": len(generated_actions),
        })

    total_actions = sum(s.get("actions_generated", 0) for s in created_schedules)

    return JSONResponse({
        "success": True,
        "schedules": created_schedules,
        "count": len(created_schedules),
        "actions_generated": total_actions,
        "message": f"Created {len(created_schedules)} recurring schedule(s) with {total_actions} upcoming actions",
    })


# ============================================================================
# Update
# ============================================================================

@router.put("/{schedule_id}")
async def update_recurring_schedule(
    project_id: str,
    schedule_id: str,
    request: Request,
    db: Session = Depends(get_db),
):
    """
    Update a recurring schedule.
    """
    schedule = db.query(RecurringSchedule).filter_by(
        id=schedule_id,
        project_id=project_id,
    ).first()

    if not schedule:
        raise HTTPException(status_code=404, detail="Schedule not found")

    data = await request.json()
    service = get_recurring_schedule_service(db)

    # Build update kwargs
    update_kwargs = {}
    for field in ["name", "weekly_pattern", "interval_type", "cycle_time",
                  "include_download", "auto_increment_index", "timezone", "unit_id"]:
        if field in data:
            update_kwargs[field] = data[field]

    updated = service.update_schedule(schedule_id, **update_kwargs)

    return {
        "success": True,
        "schedule_id": updated.id,
        "message": "Schedule updated successfully",
    }


# ============================================================================
# Delete
# ============================================================================

@router.delete("/{schedule_id}")
async def delete_recurring_schedule(
    project_id: str,
    schedule_id: str,
    db: Session = Depends(get_db),
):
    """
    Delete a recurring schedule.
    """
    service = get_recurring_schedule_service(db)
    deleted = service.delete_schedule(schedule_id)

    if not deleted:
        raise HTTPException(status_code=404, detail="Schedule not found")

    return {
        "success": True,
        "message": "Schedule deleted successfully",
    }


# ============================================================================
# Enable/Disable
# ============================================================================

@router.post("/{schedule_id}/enable")
async def enable_schedule(
    project_id: str,
    schedule_id: str,
    db: Session = Depends(get_db),
):
    """
    Enable a disabled schedule.
    """
    service = get_recurring_schedule_service(db)
    schedule = service.enable_schedule(schedule_id)

    if not schedule:
        raise HTTPException(status_code=404, detail="Schedule not found")

    return {
        "success": True,
        "schedule_id": schedule.id,
        "enabled": schedule.enabled,
        "message": "Schedule enabled",
    }


@router.post("/{schedule_id}/disable")
async def disable_schedule(
    project_id: str,
    schedule_id: str,
    db: Session = Depends(get_db),
):
    """
    Disable a schedule and cancel all its pending actions.
    """
    service = get_recurring_schedule_service(db)

    # Count pending actions before disabling (for response message)
    from sqlalchemy import and_
    from backend.models import ScheduledAction
    pending_count = db.query(ScheduledAction).filter(
        and_(
            ScheduledAction.execution_status == "pending",
            ScheduledAction.notes.like(f'%"schedule_id": "{schedule_id}"%'),
        )
    ).count()

    schedule = service.disable_schedule(schedule_id)

    if not schedule:
        raise HTTPException(status_code=404, detail="Schedule not found")

    message = "Schedule disabled"
    if pending_count > 0:
        message += f" and {pending_count} pending action(s) cancelled"

    return {
        "success": True,
        "schedule_id": schedule.id,
        "enabled": schedule.enabled,
        "cancelled_actions": pending_count,
        "message": message,
    }


# ============================================================================
# Preview Generated Actions
# ============================================================================

@router.post("/{schedule_id}/generate-preview")
async def preview_generated_actions(
    project_id: str,
    schedule_id: str,
    db: Session = Depends(get_db),
    days: int = Query(7, ge=1, le=30),
):
    """
    Preview what actions would be generated without saving them.
    """
    schedule = db.query(RecurringSchedule).filter_by(
        id=schedule_id,
        project_id=project_id,
    ).first()

    if not schedule:
        raise HTTPException(status_code=404, detail="Schedule not found")

    service = get_recurring_schedule_service(db)
    actions = service.generate_actions_for_schedule(
        schedule,
        horizon_days=days,
        preview_only=True,
    )

    return {
        "schedule_id": schedule_id,
        "schedule_name": schedule.name,
        "preview_days": days,
        "actions": [
            {
                "action_type": a.action_type,
                "scheduled_time": a.scheduled_time.isoformat(),
                "notes": a.notes,
            }
            for a in actions
        ],
        "action_count": len(actions),
    }


# ============================================================================
# Manual Generation Trigger
# ============================================================================

@router.post("/{schedule_id}/generate")
async def generate_actions_now(
    project_id: str,
    schedule_id: str,
    db: Session = Depends(get_db),
    days: int = Query(7, ge=1, le=30),
):
    """
    Manually trigger action generation for a schedule.
    """
    schedule = db.query(RecurringSchedule).filter_by(
        id=schedule_id,
        project_id=project_id,
    ).first()

    if not schedule:
        raise HTTPException(status_code=404, detail="Schedule not found")

    if not schedule.enabled:
        raise HTTPException(status_code=400, detail="Schedule is disabled")

    service = get_recurring_schedule_service(db)
    actions = service.generate_actions_for_schedule(
        schedule,
        horizon_days=days,
        preview_only=False,
    )

    return {
        "success": True,
        "schedule_id": schedule_id,
        "generated_count": len(actions),
        "message": f"Generated {len(actions)} scheduled actions",
    }


# ============================================================================
# HTML Partials
# ============================================================================

@router.get("/partials/list", response_class=HTMLResponse)
async def get_schedule_list_partial(
    project_id: str,
    request: Request,
    db: Session = Depends(get_db),
):
    """
    Return HTML partial for schedule list.
    """
    project = db.query(Project).filter_by(id=project_id).first()
    project_status = project.status if project else "active"

    schedules = db.query(RecurringSchedule).filter_by(
        project_id=project_id
    ).order_by(RecurringSchedule.created_at.desc()).all()

    # Enrich with location info
    schedule_data = []
    for s in schedules:
        location = db.query(MonitoringLocation).filter_by(id=s.location_id).first()
        schedule_data.append({
            "schedule": s,
            "location": location,
            "pattern": json.loads(s.weekly_pattern) if s.weekly_pattern else None,
        })

    return templates.TemplateResponse("partials/projects/recurring_schedule_list.html", {
        "request": request,
        "project_id": project_id,
        "schedules": schedule_data,
        "project_status": project_status,
    })
