from __future__ import annotations

import csv
import io
from datetime import datetime, timezone
from typing import Any, Callable

from .db import Database


Clock = Callable[[], datetime]


def _required_text(value: Any, field_name: str) -> str:
    text = str(value or "").strip()
    if not text:
        raise ValueError(f"{field_name} is required")
    return text


def _optional_text(value: Any) -> str:
    return str(value or "").strip()


def _report_status(value: Any) -> str:
    status = str(value or "normal").strip()
    allowed = {"normal", "risk", "need_help"}
    if status not in allowed:
        raise ValueError("report_status is invalid")
    return status


class ReportService:
    def __init__(self, database: Database, clock: Clock | None = None):
        self.database = database
        self.clock = clock or (lambda: datetime.now(timezone.utc))

    def upsert_report(self, data: dict[str, Any]) -> dict[str, Any]:
        feishu_user_id = _required_text(data.get("feishu_user_id"), "feishu_user_id")
        report_date = _required_text(data.get("report_date"), "report_date")
        today_done = _required_text(data.get("today_done"), "today_done")
        tomorrow_plan = _required_text(data.get("tomorrow_plan"), "tomorrow_plan")
        employee = self.database.find_employee(feishu_user_id)
        if not employee or employee.get("active") != 1:
            raise ValueError("employee is not active")

        now = self.clock().isoformat()
        self.database.upsert_report(
            {
                "feishu_user_id": feishu_user_id,
                "employee_name": employee["name"],
                "report_date": report_date,
                "report_status": _report_status(data.get("report_status")),
                "today_done": today_done,
                "tomorrow_plan": tomorrow_plan,
                "blockers": _optional_text(data.get("blockers")),
                "help_needed": _optional_text(data.get("help_needed")),
                "submitted_at": now,
                "updated_at": now,
            }
        )
        return next(
            report
            for report in self.database.list_reports_for_employee(feishu_user_id, 1)
            if report["feishu_user_id"] == feishu_user_id
        )

    def list_reports_for_date(self, report_date: str) -> dict[str, Any]:
        date = _required_text(report_date, "report_date")
        employees = self.database.list_report_required_employees()
        required_ids = {employee["feishu_user_id"] for employee in employees}
        reports = [
            report
            for report in self.database.list_reports_for_date(date)
            if report["feishu_user_id"] in required_ids
        ]
        submitted_ids = {report["feishu_user_id"] for report in reports}
        missing = [employee for employee in employees if employee["feishu_user_id"] not in submitted_ids]
        return {
            "date": date,
            "expectedCount": len(employees),
            "submittedCount": len(reports),
            "missingCount": len(missing),
            "reports": reports,
            "missing": missing,
        }

    def export_reports_csv(self, report_date: str) -> str:
        result = self.list_reports_for_date(report_date)
        output = io.StringIO()
        fieldnames = [
            "employee_name",
            "report_date",
            "today_done",
            "tomorrow_plan",
            "blockers",
            "help_needed",
            "report_status",
            "submitted_at",
        ]
        writer = csv.DictWriter(output, fieldnames=fieldnames, extrasaction="ignore")
        writer.writeheader()
        writer.writerows(result["reports"])
        return output.getvalue()

    def list_employee_history(self, feishu_user_id: str, limit: int = 10) -> dict[str, Any]:
        user_id = _required_text(feishu_user_id, "feishu_user_id")
        employee = self.database.find_employee(user_id)
        if not employee or employee.get("active") != 1:
            raise ValueError("employee is not active")

        return {
            "employee": employee,
            "reports": self.database.list_reports_for_employee(user_id, limit),
        }

    def previous_report_reference(self, feishu_user_id: str, before_date: str) -> dict[str, Any]:
        user_id = _required_text(feishu_user_id, "feishu_user_id")
        date = _required_text(before_date, "report_date")
        employee = self.database.find_employee(user_id)
        if not employee or employee.get("active") != 1:
            raise ValueError("employee is not active")
        return {
            "employee": employee,
            "report": self.database.find_previous_report(user_id, date),
        }

    def is_admin(self, feishu_user_id: str) -> bool:
        user_id = _required_text(feishu_user_id, "feishu_user_id")
        employee = self.database.find_employee(user_id)
        return bool(employee and employee.get("active") == 1 and employee.get("role") == "admin")
