"""客户风控数据库（MySQL 优先，SQLite 兜底）"""
import os
import sqlite3
import json
from datetime import datetime
from pathlib import Path
from typing import Dict, Any
from dotenv import load_dotenv

load_dotenv()

_DB_TYPE = os.getenv("DB_TYPE", "sqlite").lower()
_MYSQL_HOST = os.getenv("MYSQL_HOST", "127.0.0.1")
_MYSQL_PORT = int(os.getenv("MYSQL_PORT", "3306"))
_MYSQL_USER = os.getenv("MYSQL_USER", "root")
_MYSQL_PASSWORD = os.getenv("MYSQL_PASSWORD", "")
_MYSQL_DATABASE = os.getenv("MYSQL_DATABASE", "ai_cs")


def _is_mysql() -> bool:
    return _DB_TYPE in ("mysql", "mariadb")


class CustomerRiskDB:
    def __init__(self, sqlite_path: str = "db/customer_risk_db/risk.db"):
        self.sqlite_path = Path(sqlite_path)
        self.backend = "mysql" if _is_mysql() else "sqlite"
        self._sqlite_in_memory = False
        try:
            self._ensure_db()
        except Exception:
            # MySQL 不可用时自动回退，避免主流程被数据库连接拖垮
            self.backend = "sqlite"
            try:
                self._ensure_sqlite_db()
            except Exception:
                # 最后兜底：内存 SQLite，保证模块可导入
                self._sqlite_in_memory = True
                self._ensure_sqlite_db()

    def _get_mysql_conn(self):
        import pymysql
        return pymysql.connect(
            host=_MYSQL_HOST,
            port=_MYSQL_PORT,
            user=_MYSQL_USER,
            password=_MYSQL_PASSWORD,
            database=_MYSQL_DATABASE,
            charset="utf8mb4",
            cursorclass=pymysql.cursors.DictCursor,
            autocommit=False,
        )

    def _get_sqlite_conn(self):
        if self._sqlite_in_memory:
            conn = sqlite3.connect(":memory:")
        else:
            self.sqlite_path.parent.mkdir(parents=True, exist_ok=True)
            conn = sqlite3.connect(str(self.sqlite_path))
        conn.row_factory = sqlite3.Row
        return conn

    def _ensure_db(self):
        if self.backend == "mysql":
            with self._get_mysql_conn() as conn:
                with conn.cursor() as cur:
                    cur.execute(
                        """
                        CREATE TABLE IF NOT EXISTS customer_risk_profile (
                            customer_id VARCHAR(128) PRIMARY KEY,
                            do_not_serve TINYINT(1) NOT NULL DEFAULT 0,
                            risk_level VARCHAR(16) NOT NULL DEFAULT 'low',
                            risk_score INT NOT NULL DEFAULT 0,
                            note TEXT,
                            tags_json TEXT,
                            updated_at DATETIME NOT NULL
                        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
                        """
                    )
                    cur.execute(
                        """
                        CREATE TABLE IF NOT EXISTS customer_risk_event (
                            id BIGINT PRIMARY KEY AUTO_INCREMENT,
                            customer_id VARCHAR(128) NOT NULL,
                            event_type VARCHAR(32) NOT NULL,
                            event_count INT NOT NULL DEFAULT 1,
                            note TEXT,
                            created_at DATETIME NOT NULL,
                            INDEX idx_customer_time (customer_id, created_at),
                            INDEX idx_event_type (event_type)
                        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
                        """
                    )
                conn.commit()
            return
        self._ensure_sqlite_db()

    def _ensure_sqlite_db(self):
        with self._get_sqlite_conn() as conn:
            cur = conn.cursor()
            cur.execute(
                """
                CREATE TABLE IF NOT EXISTS customer_risk_profile (
                    customer_id TEXT PRIMARY KEY,
                    do_not_serve INTEGER NOT NULL DEFAULT 0,
                    risk_level TEXT NOT NULL DEFAULT 'low',
                    risk_score INTEGER NOT NULL DEFAULT 0,
                    note TEXT,
                    tags_json TEXT,
                    updated_at TEXT NOT NULL
                )
                """
            )
            cur.execute(
                """
                CREATE TABLE IF NOT EXISTS customer_risk_event (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    customer_id TEXT NOT NULL,
                    event_type TEXT NOT NULL,
                    event_count INTEGER NOT NULL DEFAULT 1,
                    note TEXT,
                    created_at TEXT NOT NULL
                )
                """
            )
            cur.execute("CREATE INDEX IF NOT EXISTS idx_customer_time ON customer_risk_event(customer_id, created_at)")
            cur.execute("CREATE INDEX IF NOT EXISTS idx_event_type ON customer_risk_event(event_type)")
            conn.commit()

    def record_event(self, customer_id: str, event_type: str, event_count: int = 1, note: str = ""):
        if not customer_id or not event_type:
            return
        now = datetime.now()
        if self.backend == "mysql":
            with self._get_mysql_conn() as conn:
                with conn.cursor() as cur:
                    cur.execute(
                        """
                        INSERT INTO customer_risk_event (customer_id, event_type, event_count, note, created_at)
                        VALUES (%s, %s, %s, %s, %s)
                        """,
                        (customer_id, event_type, int(max(1, event_count)), note, now.strftime("%Y-%m-%d %H:%M:%S")),
                    )
                conn.commit()
            return
        with self._get_sqlite_conn() as conn:
            cur = conn.cursor()
            cur.execute(
                """
                INSERT INTO customer_risk_event (customer_id, event_type, event_count, note, created_at)
                VALUES (?, ?, ?, ?, ?)
                """,
                (customer_id, event_type, int(max(1, event_count)), note, now.isoformat()),
            )
            conn.commit()

    def set_profile(
        self,
        customer_id: str,
        *,
        do_not_serve: bool = False,
        risk_level: str = "low",
        risk_score: int = 0,
        note: str = "",
        tags: list | None = None,
    ):
        if not customer_id:
            return
        tags_json = json.dumps(tags or [], ensure_ascii=False)
        now = datetime.now()
        if self.backend == "mysql":
            with self._get_mysql_conn() as conn:
                with conn.cursor() as cur:
                    cur.execute(
                        """
                        REPLACE INTO customer_risk_profile
                        (customer_id, do_not_serve, risk_level, risk_score, note, tags_json, updated_at)
                        VALUES (%s, %s, %s, %s, %s, %s, %s)
                        """,
                        (
                            customer_id,
                            1 if do_not_serve else 0,
                            risk_level,
                            int(max(0, risk_score)),
                            note,
                            tags_json,
                            now.strftime("%Y-%m-%d %H:%M:%S"),
                        ),
                    )
                conn.commit()
            return
        with self._get_sqlite_conn() as conn:
            cur = conn.cursor()
            cur.execute(
                """
                INSERT INTO customer_risk_profile
                (customer_id, do_not_serve, risk_level, risk_score, note, tags_json, updated_at)
                VALUES (?, ?, ?, ?, ?, ?, ?)
                ON CONFLICT(customer_id) DO UPDATE SET
                    do_not_serve=excluded.do_not_serve,
                    risk_level=excluded.risk_level,
                    risk_score=excluded.risk_score,
                    note=excluded.note,
                    tags_json=excluded.tags_json,
                    updated_at=excluded.updated_at
                """,
                (
                    customer_id,
                    1 if do_not_serve else 0,
                    risk_level,
                    int(max(0, risk_score)),
                    note,
                    tags_json,
                    now.isoformat(),
                ),
            )
            conn.commit()

    def _sum_events(self, customer_id: str, event_type: str, days: int) -> int:
        if self.backend == "mysql":
            with self._get_mysql_conn() as conn:
                with conn.cursor() as cur:
                    cur.execute(
                        """
                        SELECT COALESCE(SUM(event_count), 0) AS total
                        FROM customer_risk_event
                        WHERE customer_id=%s
                          AND event_type=%s
                          AND created_at >= (NOW() - INTERVAL %s DAY)
                        """,
                        (customer_id, event_type, int(max(1, days))),
                    )
                    row = cur.fetchone() or {}
                    return int(row.get("total") or 0)
        with self._get_sqlite_conn() as conn:
            cur = conn.cursor()
            cur.execute(
                """
                SELECT COALESCE(SUM(event_count), 0) AS total
                FROM customer_risk_event
                WHERE customer_id=?
                  AND event_type=?
                  AND created_at >= datetime('now', ?)
                """,
                (customer_id, event_type, f"-{int(max(1, days))} day"),
            )
            row = cur.fetchone()
            return int((row["total"] if row else 0) or 0)

    def get_profile(self, customer_id: str) -> Dict[str, Any]:
        out = {
            "customer_id": customer_id,
            "do_not_serve": False,
            "risk_level": "low",
            "risk_score": 0,
            "note": "",
            "tags": [],
        }
        if self.backend == "mysql":
            with self._get_mysql_conn() as conn:
                with conn.cursor() as cur:
                    cur.execute(
                        """
                        SELECT customer_id, do_not_serve, risk_level, risk_score, note, tags_json
                        FROM customer_risk_profile
                        WHERE customer_id=%s
                        LIMIT 1
                        """,
                        (customer_id,),
                    )
                    row = cur.fetchone()
            if not row:
                return out
            out.update(
                {
                    "do_not_serve": bool(row.get("do_not_serve")),
                    "risk_level": str(row.get("risk_level") or "low"),
                    "risk_score": int(row.get("risk_score") or 0),
                    "note": str(row.get("note") or ""),
                    "tags": json.loads(row.get("tags_json") or "[]"),
                }
            )
            return out
        with self._get_sqlite_conn() as conn:
            cur = conn.cursor()
            cur.execute(
                """
                SELECT customer_id, do_not_serve, risk_level, risk_score, note, tags_json
                FROM customer_risk_profile
                WHERE customer_id=?
                LIMIT 1
                """,
                (customer_id,),
            )
            row = cur.fetchone()
            if not row:
                return out
            out.update(
                {
                    "do_not_serve": bool(row["do_not_serve"]),
                    "risk_level": str(row["risk_level"] or "low"),
                    "risk_score": int(row["risk_score"] or 0),
                    "note": str(row["note"] or ""),
                    "tags": json.loads(row["tags_json"] or "[]"),
                }
            )
            return out

    def evaluate_customer(self, customer_id: str) -> Dict[str, Any]:
        profile = self.get_profile(customer_id)
        refund_30d = self._sum_events(customer_id, "refund", 30)
        unpaid_7d = self._sum_events(customer_id, "unpaid_order", 7)
        bad_review_90d = self._sum_events(customer_id, "bad_review", 90)

        score = int(profile.get("risk_score") or 0)
        score += refund_30d * 20
        score += unpaid_7d * 8
        score += bad_review_90d * 15

        level = "low"
        if score >= 70:
            level = "high"
        elif score >= 35:
            level = "medium"

        return {
            **profile,
            "refund_30d": refund_30d,
            "unpaid_7d": unpaid_7d,
            "bad_review_90d": bad_review_90d,
            "computed_score": score,
            "computed_level": level,
        }


risk_db = CustomerRiskDB()
