"""
Scheduler endpoints: schedule trading instances to auto-deploy at specific times.

- Kalshi only (esports matches, crypto events, etc.)
- User sets a start time and end time
- At start_time, the system deploys a trading instance with saved config
- At end_time, the system stops the instance
"""

import json
import re
import secrets
from datetime import datetime

from fastapi import APIRouter, Depends, Request
from fastapi.responses import Response
from redis.asyncio import Redis

from app.dependencies import get_current_user, get_redis
from app.modules.user.models import User

router = APIRouter()


def _job_key(user_id: int, job_id: str) -> str:
    return f"user:{user_id}:schedule:{job_id}"


def _job_list_key(user_id: int) -> str:
    return f"user:{user_id}:schedules"


@router.post("/create")
async def create_schedule(
    request: Request,
    current_user: User = Depends(get_current_user),
    redis: Redis = Depends(get_redis),
):
    """Create a scheduled trading job."""
    body = await request.json()
    raw_markets = body.get("markets", [])
    start_time = body.get("start_time", "")
    end_time = body.get("end_time", "")
    config = body.get("config", {})

    # Normalize: accept single string or list
    if isinstance(raw_markets, str):
        raw_markets = [raw_markets]
    markets = [m.strip().upper() for m in raw_markets if m and m.strip()]

    if not markets:
        return Response(
            json.dumps({"detail": "At least one market ticker is required."}),
            status_code=400, media_type="application/json",
        )

    if len(markets) > 2:
        return Response(
            json.dumps({"detail": "Maximum 2 markets allowed."}),
            status_code=400, media_type="application/json",
        )

    # Basic ticker validation
    for market in markets:
        if not re.match(r'^[A-Z0-9\-]+$', market) or len(market) < 5:
            return Response(
                json.dumps({"detail": f"Invalid market ticker format: {market}"}),
                status_code=400, media_type="application/json",
            )

    # Parse and validate times
    try:
        start_dt = datetime.fromisoformat(start_time)
        end_dt = datetime.fromisoformat(end_time)
    except (ValueError, TypeError):
        return Response(
            json.dumps({"detail": "Invalid datetime format. Use ISO format (YYYY-MM-DDTHH:MM)."}),
            status_code=400, media_type="application/json",
        )

    if end_dt <= start_dt:
        return Response(
            json.dumps({"detail": "End time must be after start time."}),
            status_code=400, media_type="application/json",
        )

    # Max 24h trading window
    if (end_dt - start_dt).total_seconds() > 86400:
        return Response(
            json.dumps({"detail": "Trading window cannot exceed 24 hours."}),
            status_code=400, media_type="application/json",
        )

    job_id = secrets.token_hex(8)
    job_data = {
        "id": job_id,
        "markets": markets,
        "start_time": start_time,
        "end_time": end_time,
        "config": config,
        "status": "pending",
        "created_at": datetime.utcnow().isoformat(),
    }

    # Store with TTL = time until end + 1 hour buffer
    ttl_seconds = int((end_dt - datetime.utcnow()).total_seconds()) + 3600
    if ttl_seconds < 60:
        ttl_seconds = 3600  # minimum 1 hour

    await redis.set(
        _job_key(current_user.id, job_id),
        json.dumps(job_data),
        ex=ttl_seconds,
    )
    await redis.sadd(_job_list_key(current_user.id), job_id)

    # Add to global pending schedule sorted set for the poller to pick up
    # Score = start timestamp so the poller processes them in order
    await redis.zadd(
        "luckyst:schedule:pending",
        {json.dumps({"job_id": job_id, "user_id": current_user.id}): start_dt.timestamp()},
    )

    return job_data


@router.get("/list")
async def list_schedules(
    current_user: User = Depends(get_current_user),
    redis: Redis = Depends(get_redis),
):
    """List all scheduled jobs for the current user."""
    job_ids = await redis.smembers(_job_list_key(current_user.id))
    jobs = []

    for jid in job_ids:
        jid_str = jid if isinstance(jid, str) else jid.decode()
        data = await redis.get(_job_key(current_user.id, jid_str))
        if data:
            jobs.append(json.loads(data))
        else:
            await redis.srem(_job_list_key(current_user.id), jid_str)

    # Sort by start_time
    jobs.sort(key=lambda j: j.get("start_time", ""))
    return {"jobs": jobs}


@router.delete("/{job_id}")
async def delete_schedule(
    job_id: str,
    current_user: User = Depends(get_current_user),
    redis: Redis = Depends(get_redis),
):
    """Delete a scheduled job."""
    key = _job_key(current_user.id, job_id)
    data = await redis.get(key)
    if not data:
        return Response(
            json.dumps({"detail": "Schedule not found"}),
            status_code=404, media_type="application/json",
        )

    await redis.delete(key)
    await redis.srem(_job_list_key(current_user.id), job_id)

    # Remove from global pending set
    # Need to scan for the matching entry
    pending = await redis.zrange("luckyst:schedule:pending", 0, -1)
    for entry in pending:
        entry_str = entry if isinstance(entry, str) else entry.decode()
        try:
            parsed = json.loads(entry_str)
            if parsed.get("job_id") == job_id and parsed.get("user_id") == current_user.id:
                await redis.zrem("luckyst:schedule:pending", entry_str)
                break
        except (json.JSONDecodeError, AttributeError):
            pass

    return {"ok": True, "id": job_id}
