import json
from datetime import date, datetime

import pytest
from openpyxl import load_workbook
from pydantic import ValidationError
from sqlalchemy import create_engine
from sqlalchemy.orm import Session

from app.api.routes import admin_imports
from app.api.routes.admin_imports import (
    COL_ISSUE_DATE,
    COL_COURSE_END_DATE,
    COL_COURSE_NAME,
    COL_COURSE_START_DATE,
    COL_NAME,
    COL_PHONE,
    COL_PROJECT,
    COL_STAGE_NAME,
    TEMPLATE_HEADERS,
    build_import_template_workbook,
    date_is_valid,
    detect_template_from_workbook,
    normalize_row_data,
    parse_issue_date,
    row_errors,
    write_error_report,
)
from app.db.base import Base
from app.models import ImportBatch, ImportBatchRow
from app.schemas.certificate import CertificateCreate


def test_parse_issue_date_accepts_common_formats():
    assert parse_issue_date("2026-06-01") == date(2026, 6, 1)
    assert parse_issue_date("2026/06/01") == date(2026, 6, 1)
    assert parse_issue_date("2026-06-01 00:00:00") == date(2026, 6, 1)
    assert parse_issue_date("2026-06-01T08:30:00") == date(2026, 6, 1)


def test_normalize_row_data_removes_time_from_excel_dates():
    row = normalize_row_data(
        {
            COL_NAME: "张三",
            COL_COURSE_START_DATE: datetime(2026, 6, 1, 0, 0, 0),
            COL_ISSUE_DATE: date(2026, 7, 5),
        }
    )

    assert row[COL_COURSE_START_DATE] == "2026-06-01"
    assert row[COL_ISSUE_DATE] == "2026-07-05"


def test_detect_template_from_workbook_uses_excel_headers(tmp_path):
    source_path = tmp_path / "practice-camp.xlsx"
    build_import_template_workbook("practice-camp").save(source_path)

    detected = detect_template_from_workbook(source_path)

    assert detected is not None
    assert detected.code == "practice-camp"


def test_row_errors_require_project_code_to_exist():
    row = {
        COL_NAME: "张三",
        COL_PHONE: "13800000000",
        COL_PROJECT: "BAD",
        COL_COURSE_START_DATE: "2026-05-01",
        COL_COURSE_END_DATE: "2026-05-31",
        COL_ISSUE_DATE: "2026-06-01",
    }
    assert "项目代码不存在或已停用" in row_errors(row, {"DBY"})


def test_date_is_valid_rejects_bad_text():
    assert not date_is_valid("not-a-date")


def test_row_errors_rejects_reversed_course_period():
    row = {
        COL_NAME: "张三",
        COL_PHONE: "13800000000",
        COL_PROJECT: "DBY",
        COL_COURSE_START_DATE: "2026-06-30",
        COL_COURSE_END_DATE: "2026-06-01",
        COL_ISSUE_DATE: "2026-07-05",
    }
    assert "课程结束日期不能早于课程开始日期" in row_errors(row, {"DBY"})


def test_row_errors_explains_required_date_format():
    row = {
        COL_NAME: "张三",
        COL_PHONE: "13800000000",
        COL_PROJECT: "DBY",
        COL_COURSE_START_DATE: "2026年6月1日",
        COL_COURSE_END_DATE: "2026-06-30",
        COL_ISSUE_DATE: "2026-07-05",
    }
    assert "课程开始日期格式错误，请使用YYYY-MM-DD，例如2026-06-01" in row_errors(row, {"DBY"})


def test_import_template_contains_date_examples_and_validation():
    workbook = build_import_template_workbook()
    data_sheet = workbook["证书导入模板"]
    instruction_sheet = workbook["填写说明"]

    assert [cell.value for cell in data_sheet[1]] == TEMPLATE_HEADERS
    assert data_sheet.column_dimensions["F"].number_format == "yyyy-mm-dd"
    assert len(data_sheet.data_validations.dataValidation) == 3
    instruction_rows = {row[0].value: row for row in instruction_sheet.iter_rows(min_row=2)}
    assert instruction_rows[COL_COURSE_START_DATE][2].value == "2026-06-01"
    assert "实际开始日期" in instruction_rows[COL_COURSE_START_DATE][3].value
    assert COL_COURSE_NAME in TEMPLATE_HEADERS
    assert COL_STAGE_NAME in TEMPLATE_HEADERS


def test_error_report_flushes_pending_rows_and_exports_reason(tmp_path, monkeypatch):
    engine = create_engine("sqlite+pysqlite:///:memory:")
    Base.metadata.create_all(engine)

    def temporary_data_path(name: str):
        folder = tmp_path / name
        folder.mkdir(parents=True, exist_ok=True)
        return folder

    monkeypatch.setattr(admin_imports, "data_path", temporary_data_path)
    with Session(engine) as db:
        batch = ImportBatch(filename="bad.xlsx", file_path="/tmp/bad.xlsx", template_code="practice-camp")
        db.add(batch)
        db.flush()
        db.add(
            ImportBatchRow(
                batch_id=batch.id,
                row_no=2,
                status="failed",
                error_message="发证日期格式错误",
                raw_json=json.dumps(
                    {
                        COL_NAME: "张三",
                        COL_PHONE: "13800000000",
                        COL_PROJECT: "DBY",
                        COL_COURSE_START_DATE: "2026-06-01 00:00:00",
                        COL_ISSUE_DATE: "错误日期",
                    },
                    ensure_ascii=False,
                ),
            )
        )

        report_path = write_error_report(db, batch.id)
        rows = list(load_workbook(report_path, data_only=True).active.iter_rows(values_only=True))

    assert rows[1][0] == 2
    assert rows[1][1] == "发证日期格式错误"
    assert rows[1][2] == "张三"
    assert rows[1][5] == "2026-06-01"
    assert rows[1][-1] == "错误日期"


def test_certificate_create_rejects_reversed_course_period():
    with pytest.raises(ValidationError, match="课程结束日期不能早于课程开始日期"):
        CertificateCreate(
            learner_name="张三",
            learner_phone="13800000000",
            project_code="DBY",
            course_start_date="2026-06-30",
            course_end_date="2026-06-01",
            issue_date="2026-07-05",
        )


def test_certificate_create_has_no_issuer_field():
    certificate = CertificateCreate(
        learner_name="张三",
        learner_phone="13800000000",
        project_code="DBY",
        course_start_date="2026-06-01",
        course_end_date="2026-06-30",
        issue_date="2026-07-05",
    )

    assert "issuer_name" not in certificate.model_dump()
