"""
inbox/accounts.py — CRUD for email_accounts table.

Passwords are encrypted with AES-256-GCM (same scheme as credential_store).
"""
from __future__ import annotations

import json
import uuid
from datetime import datetime, timezone
from typing import Any

from ..database import _encrypt, _decrypt, get_pool, _rowcount


def _now() -> str:
    return datetime.now(timezone.utc).isoformat()


# ── Read ──────────────────────────────────────────────────────────────────────

async def list_accounts(user_id: str | None = None) -> list[dict]:
    """
    List email accounts with decrypted passwords.
    - user_id=None: all accounts (admin view)
    - user_id="<uuid>": accounts for this user only
    """
    pool = await get_pool()
    if user_id is None:
        rows = await pool.fetch(
            "SELECT ea.*, a.name AS agent_name, a.model AS agent_model, a.prompt AS agent_prompt, a.max_tool_calls AS agent_max_tool_calls, a.prompt_mode AS agent_prompt_mode FROM email_accounts ea"
            " LEFT JOIN agents a ON a.id = ea.agent_id"
            " ORDER BY ea.created_at"
        )
    else:
        rows = await pool.fetch(
            "SELECT ea.*, a.name AS agent_name, a.model AS agent_model, a.prompt AS agent_prompt, a.max_tool_calls AS agent_max_tool_calls, a.prompt_mode AS agent_prompt_mode FROM email_accounts ea"
            " LEFT JOIN agents a ON a.id = ea.agent_id"
            " WHERE ea.user_id = $1 ORDER BY ea.created_at",
            user_id,
        )
    return [_decrypt_row(dict(r)) for r in rows]


async def list_accounts_enabled() -> list[dict]:
    """Return all enabled accounts (used by listener on startup)."""
    pool = await get_pool()
    rows = await pool.fetch(
        "SELECT ea.*, a.name AS agent_name, a.model AS agent_model, a.prompt AS agent_prompt, a.max_tool_calls AS agent_max_tool_calls, a.prompt_mode AS agent_prompt_mode FROM email_accounts ea"
        " LEFT JOIN agents a ON a.id = ea.agent_id"
        " WHERE ea.enabled = TRUE ORDER BY ea.created_at"
    )
    return [_decrypt_row(dict(r)) for r in rows]


async def get_account(account_id: str) -> dict | None:
    pool = await get_pool()
    row = await pool.fetchrow(
        "SELECT ea.*, a.name AS agent_name, a.model AS agent_model, a.prompt AS agent_prompt, a.max_tool_calls AS agent_max_tool_calls, a.prompt_mode AS agent_prompt_mode FROM email_accounts ea"
        " LEFT JOIN agents a ON a.id = ea.agent_id"
        " WHERE ea.id = $1",
        account_id,
    )
    if row is None:
        return None
    return _decrypt_row(dict(row))


# ── Write ─────────────────────────────────────────────────────────────────────

async def create_account(
    label: str,
    account_type: str,
    imap_host: str,
    imap_port: int,
    imap_username: str,
    imap_password: str,
    smtp_host: str | None = None,
    smtp_port: int | None = None,
    smtp_username: str | None = None,
    smtp_password: str | None = None,
    agent_id: str | None = None,
    user_id: str | None = None,
    initial_load_limit: int = 200,
    monitored_folders: list[str] | None = None,
    extra_tools: list[str] | None = None,
    telegram_chat_id: str | None = None,
    telegram_keyword: str | None = None,
    enabled: bool = True,
) -> dict:
    now = _now()
    account_id = str(uuid.uuid4())
    folders_json = json.dumps(monitored_folders or ["INBOX"])
    extra_tools_json = json.dumps(extra_tools or [])

    pool = await get_pool()
    await pool.execute(
        """
        INSERT INTO email_accounts (
            id, user_id, label, account_type,
            imap_host, imap_port, imap_username, imap_password,
            smtp_host, smtp_port, smtp_username, smtp_password,
            agent_id, enabled, initial_load_done, initial_load_limit,
            monitored_folders, extra_tools, telegram_chat_id, telegram_keyword,
            paused, created_at, updated_at
        ) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23)
        """,
        account_id, user_id, label, account_type,
        imap_host, int(imap_port), imap_username, _encrypt(imap_password),
        smtp_host, int(smtp_port) if smtp_port else None,
        smtp_username, _encrypt(smtp_password) if smtp_password else None,
        agent_id, enabled, False, int(initial_load_limit),
        folders_json, extra_tools_json, telegram_chat_id or None,
        (telegram_keyword or "").lower().strip() or None,
        False, now, now,
    )
    return await get_account(account_id)


async def update_account(account_id: str, **fields) -> bool:
    """Update fields. Encrypts imap_password/smtp_password if provided."""
    fields["updated_at"] = _now()

    if "imap_password" in fields:
        if fields["imap_password"]:
            fields["imap_password"] = _encrypt(fields["imap_password"])
        else:
            del fields["imap_password"]  # don't clear on empty string

    if "smtp_password" in fields:
        if fields["smtp_password"]:
            fields["smtp_password"] = _encrypt(fields["smtp_password"])
        else:
            del fields["smtp_password"]

    if "monitored_folders" in fields and isinstance(fields["monitored_folders"], list):
        fields["monitored_folders"] = json.dumps(fields["monitored_folders"])

    if "extra_tools" in fields and isinstance(fields["extra_tools"], list):
        fields["extra_tools"] = json.dumps(fields["extra_tools"])

    if "telegram_keyword" in fields and fields["telegram_keyword"]:
        fields["telegram_keyword"] = fields["telegram_keyword"].lower().strip() or None

    if "imap_port" in fields and fields["imap_port"] is not None:
        fields["imap_port"] = int(fields["imap_port"])
    if "smtp_port" in fields and fields["smtp_port"] is not None:
        fields["smtp_port"] = int(fields["smtp_port"])

    set_parts = []
    values: list[Any] = []
    for i, (k, v) in enumerate(fields.items(), start=1):
        set_parts.append(f"{k} = ${i}")
        values.append(v)

    id_param = len(fields) + 1
    values.append(account_id)

    pool = await get_pool()
    status = await pool.execute(
        f"UPDATE email_accounts SET {', '.join(set_parts)} WHERE id = ${id_param}",
        *values,
    )
    return _rowcount(status) > 0


async def delete_account(account_id: str) -> bool:
    pool = await get_pool()
    status = await pool.execute("DELETE FROM email_accounts WHERE id = $1", account_id)
    return _rowcount(status) > 0


async def pause_account(account_id: str) -> bool:
    pool = await get_pool()
    await pool.execute(
        "UPDATE email_accounts SET paused = TRUE, updated_at = $1 WHERE id = $2",
        _now(), account_id,
    )
    return True


async def resume_account(account_id: str) -> bool:
    pool = await get_pool()
    await pool.execute(
        "UPDATE email_accounts SET paused = FALSE, updated_at = $1 WHERE id = $2",
        _now(), account_id,
    )
    return True


async def toggle_account(account_id: str) -> bool:
    pool = await get_pool()
    await pool.execute(
        "UPDATE email_accounts SET enabled = NOT enabled, updated_at = $1 WHERE id = $2",
        _now(), account_id,
    )
    return True


async def mark_initial_load_done(account_id: str) -> None:
    pool = await get_pool()
    await pool.execute(
        "UPDATE email_accounts SET initial_load_done = TRUE, updated_at = $1 WHERE id = $2",
        _now(), account_id,
    )


# ── Helpers ───────────────────────────────────────────────────────────────────

def _decrypt_row(row: dict) -> dict:
    """Decrypt password fields in-place. Safe to call on any email_accounts row."""
    if row.get("imap_password"):
        try:
            row["imap_password"] = _decrypt(row["imap_password"])
        except Exception:
            row["imap_password"] = ""
    if row.get("smtp_password"):
        try:
            row["smtp_password"] = _decrypt(row["smtp_password"])
        except Exception:
            row["smtp_password"] = None
    if row.get("monitored_folders") and isinstance(row["monitored_folders"], str):
        try:
            row["monitored_folders"] = json.loads(row["monitored_folders"])
        except Exception:
            row["monitored_folders"] = ["INBOX"]

    if isinstance(row.get("extra_tools"), str):
        try:
            row["extra_tools"] = json.loads(row["extra_tools"])
        except Exception:
            row["extra_tools"] = []
    elif row.get("extra_tools") is None:
        row["extra_tools"] = []
    # Convert UUID to str for JSON serialisation
    if row.get("id") and not isinstance(row["id"], str):
        row["id"] = str(row["id"])
    return row


def mask_account(account: dict) -> dict:
    """Return a copy safe for the API response — passwords replaced with booleans."""
    m = dict(account)
    m["imap_password"] = bool(account.get("imap_password"))
    m["smtp_password"] = bool(account.get("smtp_password"))
    return m
