from __future__ import annotations

import json
import sqlite3
import uuid
from contextlib import contextmanager
from pathlib import Path
from typing import Any, Iterator


SCHEMA_SQL = """
CREATE TABLE IF NOT EXISTS customers (
    customer_id TEXT PRIMARY KEY,
    name TEXT NOT NULL,
    primary_phone TEXT,
    birthday TEXT,
    tags_json TEXT NOT NULL DEFAULT '[]',
    notes TEXT,
    created_at TEXT NOT NULL,
    updated_at TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS customer_field_defs (
    field_key TEXT PRIMARY KEY,
    label TEXT NOT NULL,
    value_type TEXT NOT NULL,
    entity_type TEXT NOT NULL DEFAULT 'customer',
    is_required INTEGER NOT NULL DEFAULT 0,
    source TEXT NOT NULL DEFAULT 'user_confirmed',
    status TEXT NOT NULL DEFAULT 'active',
    aliases_json TEXT NOT NULL DEFAULT '[]',
    created_at TEXT NOT NULL,
    updated_at TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS customer_field_values (
    entity_type TEXT NOT NULL DEFAULT 'customer',
    entity_id TEXT NOT NULL,
    field_key TEXT NOT NULL,
    value TEXT,
    normalized_value TEXT,
    display_value TEXT,
    updated_at TEXT NOT NULL,
    PRIMARY KEY (entity_type, entity_id, field_key)
);

CREATE TABLE IF NOT EXISTS business_records (
    record_id TEXT PRIMARY KEY,
    customer_id TEXT NOT NULL,
    record_type TEXT NOT NULL,
    status TEXT,
    record_date TEXT,
    amount TEXT,
    title TEXT,
    raw_payload_json TEXT NOT NULL DEFAULT '{}',
    created_at TEXT NOT NULL,
    updated_at TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS reminders (
    reminder_id TEXT PRIMARY KEY,
    customer_id TEXT NOT NULL,
    reminder_type TEXT NOT NULL,
    title TEXT NOT NULL,
    trigger_at TEXT,
    cron_expr TEXT NOT NULL,
    message TEXT NOT NULL,
    recurring INTEGER NOT NULL DEFAULT 0,
    scheduled INTEGER NOT NULL DEFAULT 0,
    external_task_name TEXT,
    created_at TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS import_jobs (
    import_job_id TEXT PRIMARY KEY,
    source_file TEXT NOT NULL,
    mode TEXT NOT NULL,
    summary_json TEXT NOT NULL,
    created_at TEXT NOT NULL
);

CREATE INDEX IF NOT EXISTS idx_customers_name ON customers(name);
CREATE INDEX IF NOT EXISTS idx_customers_phone ON customers(primary_phone);
CREATE INDEX IF NOT EXISTS idx_field_values_lookup ON customer_field_values(field_key, normalized_value);
CREATE INDEX IF NOT EXISTS idx_records_customer ON business_records(customer_id);
CREATE INDEX IF NOT EXISTS idx_reminders_customer ON reminders(customer_id);
"""


class SQLiteRepository:
    def __init__(self, db_path: str | Path):
        self.db_path = Path(db_path)
        self.db_path.parent.mkdir(parents=True, exist_ok=True)

    @contextmanager
    def connect(self) -> Iterator[sqlite3.Connection]:
        conn = sqlite3.connect(self.db_path)
        conn.row_factory = sqlite3.Row
        try:
            yield conn
            conn.commit()
        finally:
            conn.close()

    def init_db(self) -> None:
        with self.connect() as conn:
            conn.executescript(SCHEMA_SQL)

    def save_import_job(self, source_file: str, mode: str, summary: dict[str, Any], created_at: str) -> str:
        job_id = f"imp_{uuid.uuid4().hex[:12]}"
        with self.connect() as conn:
            conn.execute(
                """
                INSERT INTO import_jobs (import_job_id, source_file, mode, summary_json, created_at)
                VALUES (?, ?, ?, ?, ?)
                """,
                (job_id, source_file, mode, json.dumps(summary, ensure_ascii=False), created_at),
            )
        return job_id

    def list_field_defs(self, entity_type: str = "customer") -> list[dict[str, Any]]:
        with self.connect() as conn:
            rows = conn.execute(
                """
                SELECT * FROM customer_field_defs
                WHERE entity_type = ?
                ORDER BY label COLLATE NOCASE
                """,
                (entity_type,),
            ).fetchall()
        results = []
        for row in rows:
            item = dict(row)
            item["aliases"] = json.loads(item.pop("aliases_json"))
            results.append(item)
        return results

    def upsert_field_def(self, field_def: dict[str, Any], now: str) -> None:
        aliases_json = json.dumps(field_def.get("aliases", []), ensure_ascii=False)
        with self.connect() as conn:
            conn.execute(
                """
                INSERT INTO customer_field_defs
                (field_key, label, value_type, entity_type, is_required, source, status, aliases_json, created_at, updated_at)
                VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                ON CONFLICT(field_key) DO UPDATE SET
                    label = excluded.label,
                    value_type = excluded.value_type,
                    entity_type = excluded.entity_type,
                    is_required = excluded.is_required,
                    source = excluded.source,
                    status = excluded.status,
                    aliases_json = excluded.aliases_json,
                    updated_at = excluded.updated_at
                """,
                (
                    field_def["field_key"],
                    field_def["label"],
                    field_def["value_type"],
                    field_def.get("entity_type", "customer"),
                    int(bool(field_def.get("is_required", False))),
                    field_def.get("source", "user_confirmed"),
                    field_def.get("status", "active"),
                    aliases_json,
                    now,
                    now,
                ),
            )

    def upsert_customer(self, payload: dict[str, Any], now: str) -> tuple[str, bool]:
        customer_id = payload.get("customer_id")
        existing = self.find_customer_for_identity(
            name=payload["name"],
            primary_phone=payload.get("primary_phone"),
            birthday=payload.get("birthday"),
        )
        created = False
        if existing:
            customer_id = existing["customer_id"]
        if not customer_id:
            customer_id = f"cus_{uuid.uuid4().hex[:12]}"
            created = True

        with self.connect() as conn:
            conn.execute(
                """
                INSERT INTO customers
                (customer_id, name, primary_phone, birthday, tags_json, notes, created_at, updated_at)
                VALUES (?, ?, ?, ?, ?, ?, ?, ?)
                ON CONFLICT(customer_id) DO UPDATE SET
                    name = excluded.name,
                    primary_phone = COALESCE(excluded.primary_phone, customers.primary_phone),
                    birthday = COALESCE(excluded.birthday, customers.birthday),
                    tags_json = excluded.tags_json,
                    notes = COALESCE(excluded.notes, customers.notes),
                    updated_at = excluded.updated_at
                """,
                (
                    customer_id,
                    payload["name"],
                    payload.get("primary_phone"),
                    payload.get("birthday"),
                    json.dumps(payload.get("tags", []), ensure_ascii=False),
                    payload.get("notes"),
                    now,
                    now,
                ),
            )
        return customer_id, created

    def find_customer_for_identity(
        self,
        *,
        name: str,
        primary_phone: str | None = None,
        birthday: str | None = None,
    ) -> dict[str, Any] | None:
        with self.connect() as conn:
            if primary_phone:
                row = conn.execute(
                    """
                    SELECT * FROM customers
                    WHERE name = ? AND primary_phone = ?
                    LIMIT 1
                    """,
                    (name, primary_phone),
                ).fetchone()
                if row:
                    return self._customer_row_to_dict(row)
            if birthday:
                row = conn.execute(
                    """
                    SELECT * FROM customers
                    WHERE name = ? AND birthday = ?
                    LIMIT 1
                    """,
                    (name, birthday),
                ).fetchone()
                if row:
                    return self._customer_row_to_dict(row)
            row = conn.execute(
                """
                SELECT * FROM customers
                WHERE name = ?
                LIMIT 1
                """,
                (name,),
            ).fetchone()
        return self._customer_row_to_dict(row) if row else None

    def get_customer(self, customer_id: str) -> dict[str, Any] | None:
        with self.connect() as conn:
            row = conn.execute("SELECT * FROM customers WHERE customer_id = ?", (customer_id,)).fetchone()
        return self._customer_row_to_dict(row) if row else None

    def list_customers(self) -> list[dict[str, Any]]:
        with self.connect() as conn:
            rows = conn.execute(
                "SELECT * FROM customers ORDER BY updated_at DESC, name COLLATE NOCASE"
            ).fetchall()
        return [self._customer_row_to_dict(row) for row in rows]

    def delete_customer(self, customer_id: str) -> bool:
        with self.connect() as conn:
            deleted = conn.execute("DELETE FROM customers WHERE customer_id = ?", (customer_id,)).rowcount
            conn.execute(
                "DELETE FROM customer_field_values WHERE entity_type = 'customer' AND entity_id = ?",
                (customer_id,),
            )
            conn.execute("DELETE FROM business_records WHERE customer_id = ?", (customer_id,))
            conn.execute("DELETE FROM reminders WHERE customer_id = ?", (customer_id,))
        return deleted > 0

    def upsert_field_value(
        self,
        *,
        entity_type: str,
        entity_id: str,
        field_key: str,
        value: str | None,
        normalized_value: str | None,
        display_value: str | None,
        now: str,
    ) -> None:
        with self.connect() as conn:
            conn.execute(
                """
                INSERT INTO customer_field_values
                (entity_type, entity_id, field_key, value, normalized_value, display_value, updated_at)
                VALUES (?, ?, ?, ?, ?, ?, ?)
                ON CONFLICT(entity_type, entity_id, field_key) DO UPDATE SET
                    value = excluded.value,
                    normalized_value = excluded.normalized_value,
                    display_value = excluded.display_value,
                    updated_at = excluded.updated_at
                """,
                (entity_type, entity_id, field_key, value, normalized_value, display_value, now),
            )

    def list_field_values(self, entity_type: str = "customer") -> list[dict[str, Any]]:
        with self.connect() as conn:
            rows = conn.execute(
                """
                SELECT * FROM customer_field_values
                WHERE entity_type = ?
                ORDER BY updated_at DESC
                """,
                (entity_type,),
            ).fetchall()
        return [dict(row) for row in rows]

    def create_business_record(self, payload: dict[str, Any], now: str) -> str:
        record_id = f"rec_{uuid.uuid4().hex[:12]}"
        with self.connect() as conn:
            conn.execute(
                """
                INSERT INTO business_records
                (record_id, customer_id, record_type, status, record_date, amount, title, raw_payload_json, created_at, updated_at)
                VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                """,
                (
                    record_id,
                    payload["customer_id"],
                    payload["record_type"],
                    payload.get("status"),
                    payload.get("record_date"),
                    payload.get("amount"),
                    payload.get("title"),
                    json.dumps(payload.get("raw_payload", {}), ensure_ascii=False),
                    now,
                    now,
                ),
            )
        return record_id

    def list_business_records(self, customer_id: str | None = None) -> list[dict[str, Any]]:
        with self.connect() as conn:
            if customer_id:
                rows = conn.execute(
                    "SELECT * FROM business_records WHERE customer_id = ? ORDER BY updated_at DESC",
                    (customer_id,),
                ).fetchall()
            else:
                rows = conn.execute(
                    "SELECT * FROM business_records ORDER BY updated_at DESC"
                ).fetchall()
        results = []
        for row in rows:
            item = dict(row)
            item["raw_payload"] = json.loads(item.pop("raw_payload_json"))
            results.append(item)
        return results

    def replace_reminders_for_customer(self, customer_id: str, reminder_type: str, reminders: list[dict[str, Any]], now: str) -> None:
        with self.connect() as conn:
            conn.execute(
                "DELETE FROM reminders WHERE customer_id = ? AND reminder_type = ?",
                (customer_id, reminder_type),
            )
            for reminder in reminders:
                reminder_id = f"rmd_{uuid.uuid4().hex[:12]}"
                conn.execute(
                    """
                    INSERT INTO reminders
                    (reminder_id, customer_id, reminder_type, title, trigger_at, cron_expr, message, recurring, scheduled, external_task_name, created_at)
                    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                    """,
                    (
                        reminder_id,
                        customer_id,
                        reminder_type,
                        reminder["title"],
                        reminder.get("trigger_at"),
                        reminder["cron_expr"],
                        reminder["message"],
                        int(bool(reminder.get("recurring", False))),
                        int(bool(reminder.get("scheduled", False))),
                        reminder.get("external_task_name"),
                        now,
                    ),
                )

    def list_reminders(self, customer_id: str | None = None) -> list[dict[str, Any]]:
        with self.connect() as conn:
            if customer_id:
                rows = conn.execute(
                    "SELECT * FROM reminders WHERE customer_id = ? ORDER BY created_at DESC",
                    (customer_id,),
                ).fetchall()
            else:
                rows = conn.execute("SELECT * FROM reminders ORDER BY created_at DESC").fetchall()
        return [dict(row) for row in rows]

    def _customer_row_to_dict(self, row: sqlite3.Row) -> dict[str, Any]:
        item = dict(row)
        item["tags"] = json.loads(item.pop("tags_json"))
        return item
