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

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

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


def _email_wrap(body: str) -> str:
    """Wrap email body in a branded layout with inline CSS."""
    return f"""\
<!DOCTYPE html>
<html lang="en">
<head><meta charset="utf-8"></head>
<body style="margin:0;padding:0;background-color:#F8FAFC;font-family:'Inter',Helvetica,Arial,sans-serif;">
  <table width="100%" cellpadding="0" cellspacing="0" style="background-color:#F8FAFC;padding:40px 0;">
    <tr><td align="center">
      <table width="480" cellpadding="0" cellspacing="0" style="background-color:#FFFFFF;border-radius:8px;border:1px solid #E2E8F0;overflow:hidden;">
        <!-- Header -->
        <tr><td style="background-color:#0F172A;padding:24px 32px;">
          <span style="color:#FFFFFF;font-size:18px;font-weight:700;letter-spacing:-0.02em;">{config.APP_NAME}</span>
        </td></tr>
        <!-- Body -->
        <tr><td style="padding:32px;color:#475569;font-size:15px;line-height:1.6;">
          {body}
        </td></tr>
        <!-- Footer -->
        <tr><td style="padding:20px 32px;border-top:1px solid #E2E8F0;text-align:center;">
          <span style="color:#94A3B8;font-size:12px;">&copy; {config.APP_NAME} &middot; You received this because you have an account.</span>
        </td></tr>
      </table>
    </td></tr>
  </table>
</body>
</html>"""


def _email_button(url: str, label: str) -> str:
    """Render a branded CTA button for email."""
    return (
        f'<table cellpadding="0" cellspacing="0" style="margin:24px 0;">'
        f'<tr><td style="background-color:#3B82F6;border-radius:6px;text-align:center;">'
        f'<a href="{url}" style="display:inline-block;padding:12px 28px;'
        f'color:#FFFFFF;font-size:15px;font-weight:600;text-decoration:none;">'
        f'{label}</a></td></tr></table>'
    )


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"),
        from_addr=payload.get("from_addr"),
    )


@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']}"

    body = (
        f'<h2 style="margin:0 0 16px;color:#0F172A;font-size:20px;">Sign in to {config.APP_NAME}</h2>'
        f"<p>Click the button below to sign in. This link expires in "
        f"{config.MAGIC_LINK_EXPIRY_MINUTES} minutes.</p>"
        f"{_email_button(link, 'Sign In')}"
        f'<p style="font-size:13px;color:#94A3B8;">If the button doesn\'t work, copy and paste this URL into your browser:</p>'
        f'<p style="font-size:13px;color:#94A3B8;word-break:break-all;">{link}</p>'
        f'<p style="font-size:13px;color:#94A3B8;">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=_email_wrap(body),
    )


@task("send_welcome")
async def handle_send_welcome(payload: dict) -> None:
    """Send welcome email to new user."""
    body = (
        f'<h2 style="margin:0 0 16px;color:#0F172A;font-size:20px;">Welcome to {config.APP_NAME}!</h2>'
        f"<p>Thanks for signing up. We're excited to have you.</p>"
        f'{_email_button(f"{config.BASE_URL}/dashboard", "Go to Dashboard")}'
    )

    await send_email(
        to=payload["email"],
        subject=f"Welcome to {config.APP_NAME}",
        html=_email_wrap(body),
    )


@task("send_waitlist_confirmation")
async def handle_send_waitlist_confirmation(payload: dict) -> None:
    """Send waitlist confirmation email."""
    body = (
        f'<h2 style="margin:0 0 16px;color:#2C1810;font-size:20px;">You\'re on the {config.APP_NAME} waitlist!</h2>'
        f"<p>Thanks for your interest in BeanFlows. We'll send you an email the moment we're ready to welcome new coffee traders.</p>"
        f'<p style="margin-top:16px;">No spam — just the launch announcement when it\'s time.</p>'
        f'<p style="margin-top:24px;font-size:13px;color:#78716C;">The {config.APP_NAME} team</p>'
    )

    await send_email(
        to=payload["email"],
        subject=f"You're on the {config.APP_NAME} waitlist",
        html=_email_wrap(body),
    )


@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())