"""
Background task worker - SQLite-based queue (no Redis needed).
"""
import asyncio
import json
import traceback
from datetime import datetime, timedelta

from .core import config, init_db, fetch_one, fetch_all, execute, send_email


# Task handlers registry
HANDLERS: dict[str, callable] = {}


def task(name: str):
    """Decorator to register a task handler."""
    def decorator(f):
        HANDLERS[name] = f
        return f
    return decorator


# =============================================================================
# Task Queue Operations
# =============================================================================

async def enqueue(task_name: str, payload: dict = None, run_at: datetime = None) -> int:
    """Add a task to the queue."""
    return await execute(
        """
        INSERT INTO tasks (task_name, payload, status, run_at, created_at)
        VALUES (?, ?, 'pending', ?, ?)
        """,
        (
            task_name,
            json.dumps(payload or {}),
            (run_at or datetime.utcnow()).isoformat(),
            datetime.utcnow().isoformat(),
        )
    )


async def get_pending_tasks(limit: int = 10) -> list[dict]:
    """Get pending tasks ready to run."""
    now = datetime.utcnow().isoformat()
    return await fetch_all(
        """
        SELECT * FROM tasks 
        WHERE status = 'pending' AND run_at <= ?
        ORDER BY run_at ASC
        LIMIT ?
        """,
        (now, limit)
    )


async def mark_complete(task_id: int) -> None:
    """Mark task as completed."""
    await execute(
        "UPDATE tasks SET status = 'complete', completed_at = ? WHERE id = ?",
        (datetime.utcnow().isoformat(), task_id)
    )


async def mark_failed(task_id: int, error: str, retries: int) -> None:
    """Mark task as failed, schedule retry if attempts remain."""
    max_retries = 3
    
    if retries < max_retries:
        # Exponential backoff: 1min, 5min, 25min
        delay = timedelta(minutes=5 ** retries)
        run_at = datetime.utcnow() + delay
        
        await execute(
            """
            UPDATE tasks 
            SET status = 'pending', error = ?, retries = ?, run_at = ?
            WHERE id = ?
            """,
            (error, retries + 1, run_at.isoformat(), task_id)
        )
    else:
        await execute(
            "UPDATE tasks SET status = 'failed', error = ? WHERE id = ?",
            (error, task_id)
        )


# =============================================================================
# Built-in Task Handlers
# =============================================================================

@task("send_email")
async def handle_send_email(payload: dict) -> None:
    """Send an email."""
    await send_email(
        to=payload["to"],
        subject=payload["subject"],
        html=payload["html"],
        text=payload.get("text"),
    )


@task("send_magic_link")
async def handle_send_magic_link(payload: dict) -> None:
    """Send magic link email."""
    link = f"{config.BASE_URL}/auth/verify?token={payload['token']}"
    
    html = f"""
    <h2>Sign in to {config.APP_NAME}</h2>
    <p>Click the link below to sign in:</p>
    <p><a href="{link}">{link}</a></p>
    <p>This link expires in {config.MAGIC_LINK_EXPIRY_MINUTES} minutes.</p>
    <p>If you didn't request this, you can safely ignore this email.</p>
    """
    
    await send_email(
        to=payload["email"],
        subject=f"Sign in to {config.APP_NAME}",
        html=html,
    )


@task("send_welcome")
async def handle_send_welcome(payload: dict) -> None:
    """Send welcome email to new user."""
    html = f"""
    <h2>Welcome to {config.APP_NAME}!</h2>
    <p>Thanks for signing up. We're excited to have you.</p>
    <p><a href="{config.BASE_URL}/dashboard">Go to your dashboard</a></p>
    """
    
    await send_email(
        to=payload["email"],
        subject=f"Welcome to {config.APP_NAME}",
        html=html,
    )


@task("cleanup_expired_tokens")
async def handle_cleanup_tokens(payload: dict) -> None:
    """Clean up expired auth tokens."""
    await execute(
        "DELETE FROM auth_tokens WHERE expires_at < ?",
        (datetime.utcnow().isoformat(),)
    )


@task("cleanup_rate_limits")
async def handle_cleanup_rate_limits(payload: dict) -> None:
    """Clean up old rate limit entries."""
    cutoff = (datetime.utcnow() - timedelta(hours=1)).isoformat()
    await execute("DELETE FROM rate_limits WHERE timestamp < ?", (cutoff,))


@task("cleanup_old_tasks")
async def handle_cleanup_tasks(payload: dict) -> None:
    """Clean up completed/failed tasks older than 7 days."""
    cutoff = (datetime.utcnow() - timedelta(days=7)).isoformat()
    await execute(
        "DELETE FROM tasks WHERE status IN ('complete', 'failed') AND created_at < ?",
        (cutoff,)
    )


# =============================================================================
# Worker Loop
# =============================================================================

async def process_task(task: dict) -> None:
    """Process a single task."""
    task_name = task["task_name"]
    task_id = task["id"]
    retries = task.get("retries", 0)
    
    handler = HANDLERS.get(task_name)
    if not handler:
        await mark_failed(task_id, f"Unknown task: {task_name}", retries)
        return
    
    try:
        payload = json.loads(task["payload"]) if task["payload"] else {}
        await handler(payload)
        await mark_complete(task_id)
        print(f"[WORKER] Completed: {task_name} (id={task_id})")
    except Exception as e:
        error = f"{e}\n{traceback.format_exc()}"
        await mark_failed(task_id, error, retries)
        print(f"[WORKER] Failed: {task_name} (id={task_id}): {e}")


async def run_worker(poll_interval: float = 1.0) -> None:
    """Main worker loop."""
    print("[WORKER] Starting...")
    await init_db()
    
    while True:
        try:
            tasks = await get_pending_tasks(limit=10)
            
            for task in tasks:
                await process_task(task)
            
            if not tasks:
                await asyncio.sleep(poll_interval)
                
        except Exception as e:
            print(f"[WORKER] Error: {e}")
            await asyncio.sleep(poll_interval * 5)


async def run_scheduler() -> None:
    """Schedule periodic cleanup tasks."""
    print("[SCHEDULER] Starting...")
    await init_db()
    
    while True:
        try:
            # Schedule cleanup tasks every hour
            await enqueue("cleanup_expired_tokens")
            await enqueue("cleanup_rate_limits")
            await enqueue("cleanup_old_tasks")
            
            await asyncio.sleep(3600)  # 1 hour
            
        except Exception as e:
            print(f"[SCHEDULER] Error: {e}")
            await asyncio.sleep(60)


if __name__ == "__main__":
    import sys
    
    if len(sys.argv) > 1 and sys.argv[1] == "scheduler":
        asyncio.run(run_scheduler())
    else:
        asyncio.run(run_worker())
