"""Запросы к таблицам intradesk_* (синхронизированные справочники)."""
from __future__ import annotations

import re
import aiosqlite


# ── Clients ───────────────────────────────────────────────────────────────────

async def upsert_client(db: aiosqlite.Connection, item: dict) -> None:
    await db.execute(
        """
        INSERT INTO intradesk_clients
            (id, name, client_type, email, address, description, updated_at, synced_at)
        VALUES (?, ?, ?, ?, ?, ?, ?, datetime('now'))
        ON CONFLICT(id) DO UPDATE SET
            name        = excluded.name,
            client_type = excluded.client_type,
            email       = excluded.email,
            address     = excluded.address,
            description = excluded.description,
            updated_at  = excluded.updated_at,
            synced_at   = excluded.synced_at
        """,
        (
            item["id"],
            item.get("name") or "",
            item.get("clientType"),
            item.get("email"),
            item.get("address"),
            item.get("description"),
            item.get("updatedAt"),
        ),
    )


async def get_all_clients(db: aiosqlite.Connection, search: str = "") -> list[dict]:
    q = "SELECT * FROM intradesk_clients"
    params: list = []
    if search:
        q += " WHERE name LIKE ?"
        params.append(f"%{search}%")
    q += " ORDER BY name"
    async with db.execute(q, params) as cur:
        rows = await cur.fetchall()
    return [dict(r) for r in rows]


async def get_client(db: aiosqlite.Connection, client_id: int) -> dict | None:
    async with db.execute(
        "SELECT * FROM intradesk_clients WHERE id = ?", (client_id,)
    ) as cur:
        row = await cur.fetchone()
    return dict(row) if row else None


# ── Users ─────────────────────────────────────────────────────────────────────

async def upsert_user(db: aiosqlite.Connection, item: dict) -> None:
    # Основная группа — первая в списке groups
    client_id: int | None = None
    groups = item.get("groups") or []
    if groups:
        client_id = groups[0].get("userGroupId")

    # Телефон — первый из списка
    phones = item.get("phoneNumbers") or []
    phone = phones[0].get("phoneNumber") if phones else item.get("phoneNumber")

    await db.execute(
        """
        INSERT INTO intradesk_users
            (id, name, first_name, last_name, middle_name, email, phone, client_id, is_archived, synced_at)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))
        ON CONFLICT(id) DO UPDATE SET
            name        = excluded.name,
            first_name  = excluded.first_name,
            last_name   = excluded.last_name,
            middle_name = excluded.middle_name,
            email       = excluded.email,
            phone       = excluded.phone,
            client_id   = excluded.client_id,
            is_archived = excluded.is_archived,
            synced_at   = excluded.synced_at
        """,
        (
            item["id"],
            item.get("name") or "",
            item.get("firstName") or "",
            item.get("lastName") or "",
            item.get("middleName") or "",
            item.get("email") or "",
            phone,
            client_id,
            1 if item.get("isArchived") else 0,
        ),
    )


async def get_users_for_client(
    db: aiosqlite.Connection, client_id: int
) -> list[dict]:
    async with db.execute(
        "SELECT * FROM intradesk_users WHERE client_id = ? ORDER BY name",
        (client_id,),
    ) as cur:
        rows = await cur.fetchall()
    return [dict(r) for r in rows]


# ── Tasks ─────────────────────────────────────────────────────────────────────

async def upsert_task(
    db: aiosqlite.Connection, item: dict, dicts: dict[int, str] | None = None
) -> None:
    dicts = dicts or {}

    status_id = item.get("status")
    status_name = dicts.get(status_id, "") if status_id else ""

    priority_id = item.get("priority")
    priority_name = dicts.get(priority_id, "") if priority_id else ""

    executor_id = item.get("executor")
    executor_name = dicts.get(executor_id, "") if executor_id else ""

    initiator_id = item.get("initiator")
    initiator_name = dicts.get(initiator_id, "") if initiator_id else ""

    await db.execute(
        """
        INSERT INTO intradesk_tasks (
            id, task_number, name, description,
            status_id, status_name,
            priority_id, priority_name,
            client_id, initiator_id, initiator_name,
            executor_id, executor_name, executor_group,
            service_name, service_full_name,
            created_at, updated_at, closed_at, created_by, updated_by,
            synced_at
        )
        VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,datetime('now'))
        ON CONFLICT(id) DO UPDATE SET
            task_number       = excluded.task_number,
            name              = excluded.name,
            description       = excluded.description,
            status_id         = excluded.status_id,
            status_name       = excluded.status_name,
            priority_id       = excluded.priority_id,
            priority_name     = excluded.priority_name,
            client_id         = excluded.client_id,
            initiator_id      = excluded.initiator_id,
            initiator_name    = excluded.initiator_name,
            executor_id       = excluded.executor_id,
            executor_name     = excluded.executor_name,
            executor_group    = excluded.executor_group,
            service_name      = excluded.service_name,
            service_full_name = excluded.service_full_name,
            created_at        = excluded.created_at,
            updated_at        = excluded.updated_at,
            closed_at         = excluded.closed_at,
            created_by        = excluded.created_by,
            updated_by        = excluded.updated_by,
            synced_at         = excluded.synced_at
        """,
        (
            item["id"],
            item.get("tasknumber"),
            item.get("name") or "",
            item.get("description") or "",
            status_id,
            status_name,
            priority_id,
            priority_name,
            item.get("clientid"),
            initiator_id,
            initiator_name,
            executor_id,
            executor_name,
            item.get("executorgroup"),
            item.get("servicename") or "",
            item.get("servicefullname") or "",
            item.get("createdat"),
            item.get("updatedat"),
            item.get("closedat"),
            item.get("createdby") or "",
            item.get("updatedby") or "",
        ),
    )


async def get_tasks_for_client(
    db: aiosqlite.Connection,
    client_id: int,
    status_filter: str = "",
    page: int = 0,
    page_size: int = 30,
) -> list[dict]:
    q = "SELECT * FROM intradesk_tasks WHERE client_id = ?"
    params: list = [client_id]
    if status_filter:
        q += " AND status_name = ?"
        params.append(status_filter)
    q += " ORDER BY updated_at DESC LIMIT ? OFFSET ?"
    params += [page_size, page * page_size]
    async with db.execute(q, params) as cur:
        rows = await cur.fetchall()
    return [dict(r) for r in rows]


async def count_tasks_for_client(
    db: aiosqlite.Connection, client_id: int, status_filter: str = ""
) -> int:
    q = "SELECT COUNT(*) FROM intradesk_tasks WHERE client_id = ?"
    params: list = [client_id]
    if status_filter:
        q += " AND status_name = ?"
        params.append(status_filter)
    async with db.execute(q, params) as cur:
        row = await cur.fetchone()
    return row[0] if row else 0


async def get_distinct_statuses(db: aiosqlite.Connection) -> list[str]:
    async with db.execute(
        "SELECT DISTINCT status_name FROM intradesk_tasks WHERE status_name != '' ORDER BY status_name"
    ) as cur:
        rows = await cur.fetchall()
    return [r[0] for r in rows]


# ── Sync log ──────────────────────────────────────────────────────────────────

async def start_sync_log(db: aiosqlite.Connection, sync_type: str) -> int:
    async with db.execute(
        "INSERT INTO intradesk_sync_log (sync_type, status) VALUES (?, 'running')",
        (sync_type,),
    ) as cur:
        log_id = cur.lastrowid
    await db.commit()
    return log_id  # type: ignore[return-value]


async def finish_sync_log(
    db: aiosqlite.Connection,
    log_id: int,
    status: str,
    records_synced: int,
    error_message: str = "",
) -> None:
    await db.execute(
        """
        UPDATE intradesk_sync_log
        SET status = ?, records_synced = ?, error_message = ?,
            finished_at = datetime('now')
        WHERE id = ?
        """,
        (status, records_synced, error_message or None, log_id),
    )
    await db.commit()


async def get_recent_sync_logs(
    db: aiosqlite.Connection, limit: int = 10
) -> list[dict]:
    async with db.execute(
        "SELECT * FROM intradesk_sync_log ORDER BY id DESC LIMIT ?", (limit,)
    ) as cur:
        rows = await cur.fetchall()
    return [dict(r) for r in rows]


async def get_last_sync(
    db: aiosqlite.Connection, sync_type: str
) -> dict | None:
    async with db.execute(
        """
        SELECT * FROM intradesk_sync_log
        WHERE sync_type = ? AND status = 'success'
        ORDER BY id DESC LIMIT 1
        """,
        (sync_type,),
    ) as cur:
        row = await cur.fetchone()
    return dict(row) if row else None


# ── Whitelist sync ────────────────────────────────────────────────────────────

def _normalize_phone(raw: str) -> str:
    """Убираем всё кроме цифр, заменяем ведущую 8 → 7 (как в боте)."""
    digits = re.sub(r"\D", "", raw or "")
    if digits.startswith("8") and len(digits) == 11:
        digits = "7" + digits[1:]
    return digits


async def sync_whitelist_from_intradesk(db: aiosqlite.Connection) -> int:
    """
    Наполнить таблицы organizations и org_users данными из intradesk_clients /
    intradesk_users.  Вызывается автоматически после синхронизации.

    Возвращает количество затронутых строк в org_users.
    """
    # 1. Загружаем существующий маппинг intradesk_id → local org_id
    org_map: dict[int, int] = {}
    async with db.execute(
        "SELECT id, intradesk_id FROM organizations WHERE intradesk_id IS NOT NULL"
    ) as cur:
        for row in await cur.fetchall():
            org_map[row[1]] = row[0]

    # 2. Для каждого intradesk_client создаём или обновляем organizations
    async with db.execute("SELECT id, name FROM intradesk_clients") as cur:
        clients = await cur.fetchall()

    for c in clients:
        iid, cname = c[0], c[1]
        if iid in org_map:
            # Обновляем имя на случай переименования в Intradesk
            await db.execute(
                "UPDATE organizations SET name = ? WHERE intradesk_id = ?",
                (cname, iid),
            )
        else:
            # Создаём новую организацию
            async with db.execute(
                "INSERT INTO organizations (name, intradesk_id) VALUES (?, ?)",
                (cname, iid),
            ) as cur:
                org_map[iid] = cur.lastrowid  # type: ignore[assignment]

    await db.commit()

    # 3. Для каждого активного intradesk_user с телефоном — upsert в org_users
    async with db.execute(
        """
        SELECT id, phone, client_id, name, first_name, last_name
        FROM intradesk_users
        WHERE phone IS NOT NULL AND phone != '' AND is_archived = 0
        """
    ) as cur:
        users = await cur.fetchall()

    count = 0
    for u in users:
        raw_phone = u[1] or ""
        phone = _normalize_phone(raw_phone)
        if len(phone) < 7:
            continue  # явно некорректный номер

        client_id = u[2]
        org_id = org_map.get(client_id) if client_id else None
        if org_id is None:
            continue  # клиент ещё не синхронизирован

        # Формируем отображаемое имя
        display = (u[3] or "").strip() or (
            f"{u[5] or ''} {u[4] or ''}".strip() or None
        )

        await db.execute(
            """
            INSERT INTO org_users (org_id, phone, display_name)
            VALUES (?, ?, ?)
            ON CONFLICT(phone) DO UPDATE SET
                org_id       = excluded.org_id,
                display_name = COALESCE(excluded.display_name, org_users.display_name)
            """,
            (org_id, phone, display),
        )
        count += 1

    await db.commit()
    return count
