from __future__ import annotations

import csv
import shutil
import zipfile
from pathlib import Path
from typing import Any, Iterable

import openpyxl

from .docx_forms import write_docx_outputs
from .product_verification import (
    DEFAULT_PROFILE_HEADERS,
    PRODUCT_LIBRARY_HEADERS,
    PRODUCT_VERIFICATION_WORKFLOW_FIELDS,
    approval_findings,
    default_profile_rows_for_output,
    product_library_rows_for_output,
)
from .static_forms import write_static_form_outputs
from .web_app import write_web_app


PRODUCT_LIBRARY_OUTPUT_HEADERS = PRODUCT_LIBRARY_HEADERS + PRODUCT_VERIFICATION_WORKFLOW_FIELDS
DEFAULT_PROFILE_OUTPUT_HEADERS = DEFAULT_PROFILE_HEADERS + [
    "approved_for_default_current",
    "approval_reason",
]
APPROVAL_DASHBOARD_HEADERS = [
    "item_id",
    "item_type",
    "status",
    "approved_for_default",
    "reason",
]
FORM_TEMPLATE_HEADERS = ["form_id", "form_type", "form_title", "sheet", "narrative_template"]
FIELD_REGISTRY_HEADERS = [
    "form_id",
    "form_type",
    "form_title",
    "sheet",
    "section",
    "field_key",
    "label",
    "default_layer",
    "required",
    "input_type",
    "quick_options",
    "context_sources",
]
PER_FORM_DEFAULT_HEADERS = [
    "lookup_key",
    "form_id",
    "field_key",
    "per_form_default_value",
    "form_title",
    "field_label",
    "default_layer",
]
FORM_SHEET_HEADERS = [
    "Section",
    "Field label",
    "Key",
    "Default layer",
    "Required",
    "Encounter input",
    "Per-form default",
    "Global default",
    "Final value preview",
    "Value source preview",
    "Input type",
    "Quick text templates",
    "History/exam context sources",
    "Help",
]
PHYSICIAN_REVIEW_DASHBOARD_HEADERS = [
    "form_id",
    "title",
    "form_type",
    "condition_tags",
    "required_fields",
    "manual_required_fields",
    "defaulted_fields",
    "mapped_product_rows",
    "review_status",
    "reviewer",
    "review_date",
    "review_notes",
]


def write_csv(path: Path, rows: Iterable[dict[str, Any]], headers: list[str]) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    with path.open("w", encoding="utf-8", newline="") as handle:
        writer = csv.DictWriter(handle, fieldnames=headers, extrasaction="ignore")
        writer.writeheader()
        writer.writerows(rows)


def approval_dashboard_rows(data: dict[str, Any]) -> list[dict[str, str]]:
    return [
        {
            "item_id": finding.item_id,
            "item_type": finding.item_type,
            "status": finding.status,
            "approved_for_default": "YES" if finding.approved_for_default else "NO",
            "reason": finding.reason,
        }
        for finding in approval_findings(data)
    ]


def physician_review_dashboard_rows(data: dict[str, Any]) -> list[dict[str, Any]]:
    product_rows = data.get("product_library", {}).get("product_rows", [])
    rows: list[dict[str, Any]] = []
    for form in data.get("forms", []):
        fields = form.get("fields", [])
        field_keys = {field.get("key", "") for field in fields}
        mapped_product_rows = sum(
            1
            for row in product_rows
            if any(key.strip() in field_keys for key in str(row[7] if len(row) > 7 else "").split(";"))
        )
        rows.append(
            {
                "form_id": form.get("form_id", ""),
                "title": form.get("title", ""),
                "form_type": form.get("form_type", ""),
                "condition_tags": form.get("condition_tags", ""),
                "required_fields": sum(1 for field in fields if field.get("required")),
                "manual_required_fields": sum(
                    1
                    for field in fields
                    if field.get("required") and field.get("default_layer") == "No Default"
                ),
                "defaulted_fields": sum(
                    1 for field in fields if field.get("default_layer") != "No Default"
                ),
                "mapped_product_rows": mapped_product_rows,
                "review_status": "Needs physician review",
                "reviewer": "",
                "review_date": "",
                "review_notes": "",
            }
        )
    return rows


def _replace_sheet(
    workbook: openpyxl.Workbook,
    sheet_name: str,
    title: str,
    headers: list[str],
    rows: list[dict[str, Any]],
) -> None:
    if sheet_name in workbook.sheetnames:
        index = workbook.sheetnames.index(sheet_name)
        workbook.remove(workbook[sheet_name])
        worksheet = workbook.create_sheet(sheet_name, index)
    else:
        worksheet = workbook.create_sheet(sheet_name)

    worksheet.append([title])
    worksheet.append([])
    worksheet.append(headers)
    for row in rows:
        worksheet.append([row.get(header, "") for header in headers])
    worksheet.freeze_panes = "A4"


def _write_workbook_copy(source: Path, destination: Path, data: dict[str, Any]) -> None:
    destination.parent.mkdir(parents=True, exist_ok=True)
    shutil.copy2(source, destination)
    workbook = openpyxl.load_workbook(destination)
    product_rows = product_library_rows_for_output(data)
    profile_rows = default_profile_rows_for_output(data)
    approval_rows = approval_dashboard_rows(data)

    _replace_sheet(
        workbook,
        "Form_Templates",
        "Epione Form Narrative Templates v0.7",
        FORM_TEMPLATE_HEADERS,
        form_template_rows(data),
    )
    _replace_sheet(
        workbook,
        "Field_Registry",
        "Epione Field Registry v0.7",
        FIELD_REGISTRY_HEADERS,
        field_registry_rows(data),
    )
    _replace_sheet(
        workbook,
        "Per_Form_Defaults",
        "Epione Per-Form Defaults v0.7",
        PER_FORM_DEFAULT_HEADERS,
        per_form_default_rows(data),
    )
    for form in data.get("forms", []):
        _replace_form_sheet(workbook, form, data.get("global_defaults", {}))

    _replace_sheet(
        workbook,
        "Product_Library",
        "Epione Product / Device / Solution Library v0.7",
        PRODUCT_LIBRARY_OUTPUT_HEADERS,
        product_rows,
    )
    _replace_sheet(
        workbook,
        "Product_Default_Profiles",
        "Epione Product Default Profiles v0.7",
        DEFAULT_PROFILE_OUTPUT_HEADERS,
        profile_rows,
    )
    _replace_sheet(
        workbook,
        "Product_Approval_Dashboard",
        "Epione Product Default Approval Dashboard v0.7",
        APPROVAL_DASHBOARD_HEADERS,
        approval_rows,
    )
    workbook.save(destination)


def form_sheet_name(form: dict[str, Any]) -> str:
    return str(
        form.get("sheet_name")
        or form.get("template_sheet")
        or form.get("form_id", "Form")
    )[:31]


def form_template_rows(data: dict[str, Any]) -> list[dict[str, Any]]:
    return [
        {
            "form_id": form.get("form_id", ""),
            "form_type": form.get("form_type", ""),
            "form_title": form.get("title", ""),
            "sheet": form_sheet_name(form),
            "narrative_template": form.get("narrative_template") or form.get("template", ""),
        }
        for form in data.get("forms", [])
    ]


def field_registry_rows(data: dict[str, Any]) -> list[dict[str, Any]]:
    rows: list[dict[str, Any]] = []
    for form in data.get("forms", []):
        for field in form.get("fields", []):
            rows.append(
                {
                    "form_id": form.get("form_id", ""),
                    "form_type": form.get("form_type", ""),
                    "form_title": form.get("title", ""),
                    "sheet": form_sheet_name(form),
                    "section": field.get("section", ""),
                    "field_key": field.get("key", ""),
                    "label": field.get("label", ""),
                    "default_layer": field.get("default_layer", ""),
                    "required": "Required" if field.get("required") else "Optional",
                    "input_type": field.get("input_type", ""),
                    "quick_options": _join_list(field.get("quick_options", "")),
                    "context_sources": _join_list(field.get("context_sources", "")),
                }
            )
    return rows


def per_form_default_rows(data: dict[str, Any]) -> list[dict[str, Any]]:
    rows: list[dict[str, Any]] = []
    for form in data.get("forms", []):
        defaults = form.get("defaults") or form.get("procedure_defaults") or {}
        fields_by_key = {field.get("key"): field for field in form.get("fields", [])}
        for key, value in defaults.items():
            field = fields_by_key.get(key, {})
            rows.append(
                {
                    "lookup_key": f"{form.get('form_id', '')}|{key}",
                    "form_id": form.get("form_id", ""),
                    "field_key": key,
                    "per_form_default_value": value,
                    "form_title": form.get("title", ""),
                    "field_label": field.get("label", key),
                    "default_layer": field.get("default_layer", "Per-Form Default"),
                }
            )
    return rows


def _replace_form_sheet(
    workbook: openpyxl.Workbook,
    form: dict[str, Any],
    global_defaults: dict[str, Any],
) -> None:
    sheet_name = form_sheet_name(form)
    if sheet_name in workbook.sheetnames:
        index = workbook.sheetnames.index(sheet_name)
        workbook.remove(workbook[sheet_name])
        worksheet = workbook.create_sheet(sheet_name, index)
    else:
        worksheet = workbook.create_sheet(sheet_name)

    worksheet.append(["Form title", form.get("title", "")])
    worksheet.append(["Form ID", form.get("form_id", "")])
    worksheet.append(["Generated EMR note (Google Apps Script writes here)", ""])
    worksheet.append(
        [
            "Instructions",
            "Enter encounter-specific values in column F. Final Value preview resolves as Encounter Input -> Per-Form Default -> Global Default -> blank if No Default. Apps Script resolves current defaults dynamically when generating/copying/exporting.",
        ]
    )
    worksheet.append(["Default rule", "Encounter input -> per-form default -> global default -> blank for No Default fields"])
    worksheet.append(["Narrative template", form.get("narrative_template") or form.get("template", "")])
    worksheet.append([])
    worksheet.append(FORM_SHEET_HEADERS)

    defaults = form.get("defaults") or form.get("procedure_defaults") or {}
    for field in form.get("fields", []):
        per_default = defaults.get(field.get("key"), "")
        global_default = global_defaults.get(field.get("key"), "")
        final_value, source = _preview_value(field, per_default, global_default)
        worksheet.append(
            [
                field.get("section", ""),
                field.get("label", ""),
                field.get("key", ""),
                field.get("default_layer", ""),
                "Required" if field.get("required") else "Optional",
                "",
                per_default,
                global_default,
                final_value,
                source,
                field.get("input_type", ""),
                _join_list(field.get("quick_options", "")),
                _join_list(field.get("context_sources", "")),
                field.get("help", ""),
            ]
        )
    worksheet.freeze_panes = "A9"
    worksheet.column_dimensions["A"].width = 24
    worksheet.column_dimensions["B"].width = 36
    worksheet.column_dimensions["C"].width = 30
    worksheet.column_dimensions["F"].width = 34
    worksheet.column_dimensions["G"].width = 42
    worksheet.column_dimensions["H"].width = 42
    worksheet.column_dimensions["I"].width = 42
    worksheet.column_dimensions["L"].width = 48
    worksheet.column_dimensions["M"].width = 34


def _join_list(value: Any) -> str:
    if isinstance(value, list):
        return "; ".join(str(item) for item in value if str(item).strip())
    return str(value or "")


def _preview_value(field: dict[str, Any], per_default: Any, global_default: Any) -> tuple[Any, str]:
    if field.get("default_layer") == "No Default":
        return "", "Manual only"
    if per_default:
        return per_default, "Per-form default"
    if global_default:
        return global_default, "Global default"
    return "", "Blank"


def write_v07_outputs(root: Path, output_dir: Path, data: dict[str, Any]) -> dict[str, str]:
    product_rows = product_library_rows_for_output(data)
    profile_rows = default_profile_rows_for_output(data)
    approval_rows = approval_dashboard_rows(data)

    csv_dir = output_dir / "Google_Sheets" / "csv_tabs"
    apps_script_dir = output_dir / "Google_Sheets" / "apps_script"
    write_csv(csv_dir / "Product_Library.csv", product_rows, PRODUCT_LIBRARY_OUTPUT_HEADERS)
    write_csv(csv_dir / "Product_Default_Profiles.csv", profile_rows, DEFAULT_PROFILE_OUTPUT_HEADERS)
    write_csv(csv_dir / "Product_Approval_Dashboard.csv", approval_rows, APPROVAL_DASHBOARD_HEADERS)
    review_dashboard = output_dir / "physician_review_dashboard.csv"
    write_csv(
        review_dashboard,
        physician_review_dashboard_rows(data),
        PHYSICIAN_REVIEW_DASHBOARD_HEADERS,
    )
    if apps_script_dir.exists():
        shutil.rmtree(apps_script_dir)
    shutil.copytree(root / "Google_Sheets" / "apps_script", apps_script_dir)
    shutil.copy2(
        root / "Google_Sheets" / "INSTALL_GOOGLE_SHEETS.md",
        output_dir / "Google_Sheets" / "INSTALL_GOOGLE_SHEETS.md",
    )

    local_workbook = output_dir / "Epione_EMR_Template_Workbook_v0.7.xlsx"
    google_workbook = output_dir / "Google_Sheets" / "Epione_Google_Sheets_Import_v0.7.xlsx"
    _write_workbook_copy(root / "Epione_EMR_Template_Workbook_v0.6.xlsx", local_workbook, data)
    _write_workbook_copy(
        root / "Google_Sheets" / "Epione_Google_Sheets_Import_v0.6.xlsx",
        google_workbook,
        data,
    )
    static_form_outputs = write_static_form_outputs(data, output_dir)
    docx_outputs = write_docx_outputs(data, output_dir)
    web_app_outputs = write_web_app(data, output_dir)

    google_package = output_dir / "Epione_Google_Sheets_Package_v0.7.zip"
    system_package = output_dir / "Epione_EMR_Template_System_v0.7.zip"
    _write_zip(
        google_package,
        [
            (google_workbook, "Epione_Google_Sheets_Import_v0.7.xlsx"),
            (output_dir / "Google_Sheets" / "INSTALL_GOOGLE_SHEETS.md", "INSTALL_GOOGLE_SHEETS.md"),
            (apps_script_dir, "apps_script"),
            (csv_dir, "csv_tabs"),
        ],
    )
    _write_system_package(root, output_dir, system_package)

    return {
        "local_workbook": str(local_workbook),
        "google_import_workbook": str(google_workbook),
        "google_csv_tabs": str(csv_dir),
        "google_sheets_package": str(google_package),
        "system_package": str(system_package),
        "physician_review_dashboard": str(review_dashboard),
        **static_form_outputs,
        **docx_outputs,
        **web_app_outputs,
    }


def _write_zip(destination: Path, entries: list[tuple[Path, str]]) -> None:
    destination.parent.mkdir(parents=True, exist_ok=True)
    if destination.exists():
        destination.unlink()
    with zipfile.ZipFile(destination, "w", compression=zipfile.ZIP_DEFLATED) as archive:
        for source, arcname in entries:
            if source.is_dir():
                for path in sorted(source.rglob("*")):
                    if path.is_file() and not _excluded_from_package(path):
                        archive.write(path, Path(arcname) / path.relative_to(source))
            else:
                archive.write(source, arcname)


def _excluded_from_package(path: Path) -> bool:
    return "__pycache__" in path.parts or path.suffix == ".pyc"


def _write_system_package(root: Path, output_dir: Path, destination: Path) -> None:
    entries: list[tuple[Path, str]] = [
        (root / "README.md", "README.md"),
        (root / "TODO.md", "TODO.md"),
        (root / "Data", "Data"),
        (root / "Documentation", "Documentation"),
        (output_dir / "HTML_App", "HTML_App"),
        (root / "src", "src"),
        (root / "scripts", "scripts"),
        (root / "tests", "tests"),
        (root / "Makefile", "Makefile"),
        (root / "pyproject.toml", "pyproject.toml"),
        (output_dir / "Epione_EMR_Template_Workbook_v0.7.xlsx", "Epione_EMR_Template_Workbook_v0.7.xlsx"),
        (output_dir / "Google_Sheets", "Google_Sheets"),
        (output_dir / "Form_RTF", "Form_RTF"),
        (output_dir / "Form_HTML", "Form_HTML"),
        (output_dir / "Procedure_DOCX", "Procedure_DOCX"),
        (output_dir / "Condition_Folders", "Condition_Folders"),
        (
            output_dir / "product_default_approval_dashboard.csv",
            "product_default_approval_dashboard.csv",
        ),
        (
            output_dir / "physician_review_dashboard.csv",
            "physician_review_dashboard.csv",
        ),
    ]
    _write_zip(destination, [(source, arcname) for source, arcname in entries if source.exists()])
