"""Extract data from Signal DB."""

import json
import os
import sqlite3
from pathlib import Path
from typing import Optional, Tuple

from pysqlcipher3 import dbapi2 as sqlcipher  # type: ignore[import]
from typer import secho

from .models import Contacts, Convos


def fetch_data(
    db_file: Path,
    key: str,
    manual: bool = False,
    chats: Optional[str] = None,
    include_empty: bool = False,
    log: bool = False,
) -> Tuple[Convos, Contacts]:
    """Load SQLite data into dicts."""
    contacts: Contacts = {}
    convos: Convos = {}
    chats_list = chats.split(",") if chats else []

    db_file_decrypted = db_file.parents[0] / "db-decrypt.sqlite"
    if manual:
        if log:
            secho(f"Manually decrypting db to {db_file_decrypted}")
        if db_file_decrypted.exists():
            db_file_decrypted.unlink()
        command = (
            f'echo "'
            f"PRAGMA key = \\\"x'{key}'\\\";"
            f"ATTACH DATABASE '{db_file_decrypted}' AS plaintext KEY '';"
            f"SELECT sqlcipher_export('plaintext');"
            f"DETACH DATABASE plaintext;"
            f'" | sqlcipher {db_file}'
        )
        os.system(command)  # NoQA: S605
        # use sqlite instead of sqlcipher as DB already decrypted
        db = sqlite3.connect(str(db_file_decrypted))
        c = db.cursor()
    else:
        db = sqlcipher.connect(str(db_file))
        c = db.cursor()
        # param binding doesn't work for pragmas, so use a direct string concat
        c.execute(f"PRAGMA KEY = \"x'{key}'\"")
        c.execute("PRAGMA cipher_page_size = 4096")
        c.execute("PRAGMA kdf_iter = 64000")
        c.execute("PRAGMA cipher_hmac_algorithm = HMAC_SHA512")
        c.execute("PRAGMA cipher_kdf_algorithm = PBKDF2_HMAC_SHA512")

    query = "SELECT type, id, e164, name, profileName, members FROM conversations"
    c.execute(query)
    for result in c:
        if log:
            secho(f"\tLoading SQL results for: {result[3]}, aka {result[4]}")
        is_group = result[0] == "group"
        cid = result[1]
        contacts[cid] = {
            "id": cid,
            "name": result[3],
            "number": result[2],
            "profileName": result[4],
            "is_group": is_group,
        }
        if contacts[cid]["name"] is None:
            contacts[cid]["name"] = contacts[cid]["profileName"]

        if not chats or (result[3] in chats_list or result[4] in chats_list):
            convos[cid] = []

    c.execute("SELECT json, conversationId FROM messages ORDER BY sent_at")
    for result in c:
        content = json.loads(result[0])
        cid = result[1]
        if cid and cid in convos:
            convos[cid].append(content)

    if db_file_decrypted.exists():
        db_file_decrypted.unlink()

    if not include_empty:
        convos = {key: val for key, val in convos.items() if len(val) > 0}

    return convos, contacts
