from __future__ import annotations

import json
import sqlite3
import threading
from pathlib import Path
from typing import Any


class Database:
    def __init__(self, database_path: Path, employee_seed_path: Path):
        database_path.parent.mkdir(parents=True, exist_ok=True)
        self.connection = sqlite3.connect(database_path, check_same_thread=False)
        self.connection.row_factory = sqlite3.Row
        self._lock = threading.RLock()
        self._migrate()
        self.load_employees(employee_seed_path)

    def close(self) -> None:
        with self._lock:
            self.connection.close()

    def _migrate(self) -> None:
        with self._lock:
            self.connection.executescript(
                """
                CREATE TABLE IF NOT EXISTS employees (
                  id INTEGER PRIMARY KEY AUTOINCREMENT,
                  feishu_user_id TEXT NOT NULL UNIQUE,
                  name TEXT NOT NULL,
                  department TEXT NOT NULL DEFAULT '',
                  manager TEXT NOT NULL DEFAULT '',
                  role TEXT NOT NULL DEFAULT 'staff',
                  active INTEGER NOT NULL DEFAULT 1,
                  created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
                  updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
                );

                CREATE TABLE IF NOT EXISTS daily_reports (
                  id INTEGER PRIMARY KEY AUTOINCREMENT,
                  feishu_user_id TEXT NOT NULL,
                  employee_name TEXT NOT NULL,
                  report_date TEXT NOT NULL,
                  report_status TEXT NOT NULL DEFAULT 'normal',
                  today_done TEXT NOT NULL,
                  tomorrow_plan TEXT NOT NULL,
                  blockers TEXT NOT NULL DEFAULT '',
                  help_needed TEXT NOT NULL DEFAULT '',
                  submitted_at TEXT NOT NULL,
                  updated_at TEXT NOT NULL,
                  UNIQUE(feishu_user_id, report_date)
                );
                """
            )
            try:
                self.connection.execute("ALTER TABLE employees ADD COLUMN role TEXT NOT NULL DEFAULT 'staff'")
            except sqlite3.OperationalError as error:
                if "duplicate column name" not in str(error).lower():
                    raise
            try:
                self.connection.execute(
                    "ALTER TABLE daily_reports ADD COLUMN report_status TEXT NOT NULL DEFAULT 'normal'"
                )
            except sqlite3.OperationalError as error:
                if "duplicate column name" not in str(error).lower():
                    raise
            self.connection.commit()

    def load_employees(self, employee_seed_path: Path) -> None:
        if not employee_seed_path.exists():
            return

        employees = json.loads(employee_seed_path.read_text(encoding="utf-8"))
        self.upsert_employees(employees)

    def upsert_employees(self, employees: list[dict[str, Any]]) -> None:
        with self._lock, self.connection:
            for employee in employees:
                role = employee.get("role")
                if role is None:
                    existing = self.connection.execute(
                        "SELECT role FROM employees WHERE feishu_user_id = ?",
                        (employee["feishu_user_id"],),
                    ).fetchone()
                    role = existing["role"] if existing else "staff"
                self.connection.execute(
                    """
                    INSERT INTO employees (feishu_user_id, name, department, manager, role, active, updated_at)
                    VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
                    ON CONFLICT(feishu_user_id) DO UPDATE SET
                      name = excluded.name,
                      department = excluded.department,
                      manager = excluded.manager,
                      role = excluded.role,
                      active = excluded.active,
                      updated_at = CURRENT_TIMESTAMP
                    """,
                    (
                        employee["feishu_user_id"],
                        employee["name"],
                        employee.get("department", ""),
                        employee.get("manager", ""),
                        role,
                        0 if employee.get("active") is False else 1,
                    ),
                )

    def upsert_employee(self, employee: dict[str, Any]) -> None:
        self.upsert_employees([employee])

    def list_active_employees(self) -> list[dict[str, Any]]:
        with self._lock:
            rows = self.connection.execute(
                """
                SELECT feishu_user_id, name, department, manager, role, active
                FROM employees
                WHERE active = 1
                ORDER BY department, name
                """
            ).fetchall()
        return [dict(row) for row in rows]

    def list_report_required_employees(self) -> list[dict[str, Any]]:
        with self._lock:
            rows = self.connection.execute(
                """
                SELECT feishu_user_id, name, department, manager, role, active
                FROM employees
                WHERE active = 1 AND role != 'admin'
                ORDER BY department, name
                """
            ).fetchall()
        return [dict(row) for row in rows]

    def find_employee(self, feishu_user_id: str) -> dict[str, Any] | None:
        with self._lock:
            row = self.connection.execute(
                """
                SELECT feishu_user_id, name, department, manager, role, active
                FROM employees
                WHERE feishu_user_id = ?
                """,
                (feishu_user_id,),
            ).fetchone()
        return dict(row) if row else None

    def upsert_report(self, report: dict[str, Any]) -> None:
        with self._lock, self.connection:
            self.connection.execute(
                """
                INSERT INTO daily_reports (
                  feishu_user_id, employee_name, report_date, report_status, today_done, tomorrow_plan,
                  blockers, help_needed, submitted_at, updated_at
                )
                VALUES (
                  :feishu_user_id, :employee_name, :report_date, :report_status, :today_done, :tomorrow_plan,
                  :blockers, :help_needed, :submitted_at, :updated_at
                )
                ON CONFLICT(feishu_user_id, report_date) DO UPDATE SET
                  employee_name = excluded.employee_name,
                  report_status = excluded.report_status,
                  today_done = excluded.today_done,
                  tomorrow_plan = excluded.tomorrow_plan,
                  blockers = excluded.blockers,
                  help_needed = excluded.help_needed,
                  updated_at = excluded.updated_at
                """,
                report,
            )

    def list_reports_for_date(self, report_date: str) -> list[dict[str, Any]]:
        with self._lock:
            rows = self.connection.execute(
                """
                SELECT id, feishu_user_id, employee_name, report_date, report_status, today_done,
                       tomorrow_plan, blockers, help_needed, submitted_at, updated_at
                FROM daily_reports
                WHERE report_date = ?
                ORDER BY employee_name
                """,
                (report_date,),
            ).fetchall()
        return [dict(row) for row in rows]

    def list_reports_for_employee(self, feishu_user_id: str, limit: int = 10) -> list[dict[str, Any]]:
        with self._lock:
            rows = self.connection.execute(
                """
                SELECT id, feishu_user_id, employee_name, report_date, report_status, today_done,
                       tomorrow_plan, blockers, help_needed, submitted_at, updated_at
                FROM daily_reports
                WHERE feishu_user_id = ?
                ORDER BY report_date DESC, updated_at DESC
                LIMIT ?
                """,
                (feishu_user_id, limit),
            ).fetchall()
        return [dict(row) for row in rows]

    def find_previous_report(self, feishu_user_id: str, before_date: str) -> dict[str, Any] | None:
        with self._lock:
            row = self.connection.execute(
                """
                SELECT id, feishu_user_id, employee_name, report_date, report_status, today_done,
                       tomorrow_plan, blockers, help_needed, submitted_at, updated_at
                FROM daily_reports
                WHERE feishu_user_id = ? AND report_date < ?
                ORDER BY report_date DESC, updated_at DESC
                LIMIT 1
                """,
                (feishu_user_id, before_date),
            ).fetchone()
        return dict(row) if row else None
