from __future__ import annotations

import json
from dataclasses import asdict
from datetime import datetime
from pathlib import Path
from typing import Any

from .exporter import export_customers, preview_as_markdown
from .models import CustomerPayload, ImportPreview
from .normalizer import (
    clean_text,
    dumps_json,
    normalize_date,
    normalize_optional_text,
    normalize_phone,
    normalize_tags,
    normalize_value_for_type,
)
from .parser import parse_file
from .query import match_filters, query_due_within_days, sort_customers
from .reminder import build_birthday_plans, build_due_plans, build_follow_up_plan, schedule_plans
from .repository import SQLiteRepository
from .schema import (
    create_schema_from_labels,
    is_builtin_field,
    resolve_builtin_field,
    resolve_grouped_record_field,
    resolve_or_create_field,
)


class CustomerManagementService:
    def __init__(self, db_path: str | Path):
        self.repo = SQLiteRepository(db_path)
        self.repo.init_db()

    def inspect_schema(self) -> dict[str, Any]:
        return {
            "builtin_fields": [
                {"field_key": "name", "label": "姓名", "value_type": "string", "required": True},
                {"field_key": "primary_phone", "label": "手机号", "value_type": "phone", "required": False},
                {"field_key": "birthday", "label": "生日", "value_type": "date", "required": False},
                {"field_key": "tags", "label": "标签", "value_type": "tags", "required": False},
                {"field_key": "notes", "label": "备注", "value_type": "string", "required": False},
            ],
            "dynamic_fields": self.repo.list_field_defs(),
        }

    def evolve_schema(self, labels: list[str]) -> dict[str, Any]:
        existing_defs = self.repo.list_field_defs()
        now = _now()
        created = []
        for field_def in create_schema_from_labels(labels, existing_defs):
            self.repo.upsert_field_def(field_def.to_dict(), now)
            created.append(field_def.to_dict())
        return {"created_fields": created}

    def add_customer(self, payload: dict[str, Any]) -> dict[str, Any]:
        customer_payload = CustomerPayload(
            name=clean_text(payload["name"]),
            primary_phone=normalize_phone(payload.get("primary_phone")),
            birthday=normalize_date(payload.get("birthday")),
            tags=normalize_tags(payload.get("tags")),
            notes=normalize_optional_text(payload.get("notes")),
            dynamic_fields=payload.get("dynamic_fields", {}),
        )
        customer_id, created = self.repo.upsert_customer(customer_payload.to_dict(), _now())
        self._save_dynamic_fields("customer", customer_id, customer_payload.dynamic_fields, source="user_confirmed")
        customer = self.get_customer(customer_id)
        return {"created": created, "customer": customer}

    def update_customer(self, match: dict[str, Any], updates: dict[str, Any]) -> dict[str, Any]:
        customer = self._resolve_single_customer(match)
        merged = {
            "customer_id": customer["customer_id"],
            "name": updates.get("name", customer["name"]),
            "primary_phone": updates.get("primary_phone", customer.get("primary_phone")),
            "birthday": updates.get("birthday", customer.get("birthday")),
            "tags": updates.get("tags", customer.get("tags", [])),
            "notes": updates.get("notes", customer.get("notes")),
        }
        customer_id, _ = self.repo.upsert_customer(
            {
                "customer_id": customer["customer_id"],
                "name": clean_text(merged["name"]),
                "primary_phone": normalize_phone(merged.get("primary_phone")),
                "birthday": normalize_date(merged.get("birthday")),
                "tags": normalize_tags(merged.get("tags")),
                "notes": normalize_optional_text(merged.get("notes")),
            },
            _now(),
        )
        self._save_dynamic_fields("customer", customer_id, updates.get("dynamic_fields", {}), source="user_confirmed")
        return {"customer": self.get_customer(customer_id)}

    def delete_customer(self, match: dict[str, Any]) -> dict[str, Any]:
        customer = self._resolve_single_customer(match)
        deleted = self.repo.delete_customer(customer["customer_id"])
        return {"deleted": deleted, "customer": customer}

    def get_customer(self, customer_id: str) -> dict[str, Any] | None:
        customers = self._load_customer_views()
        for customer in customers:
            if customer["customer_id"] == customer_id:
                return customer
        return None

    def query_customers(self, query: dict[str, Any]) -> dict[str, Any]:
        customers = self._load_customer_views()
        if query.get("due_within_days"):
            matched = query_due_within_days(customers, int(query["due_within_days"]))
        else:
            matched = [customer for customer in customers if match_filters(customer, query)]
        matched = sort_customers(matched)
        offset = max(int(query.get("offset", 0)), 0)
        limit = int(query.get("limit", 20))
        summarized = [self._summarize_customer(customer) for customer in matched[offset : offset + limit]]
        return {
            "count": len(matched),
            "items": summarized,
            "offset": offset,
            "limit": limit,
            "markdown": preview_as_markdown(summarized, limit=limit),
        }

    def add_record(self, payload: dict[str, Any]) -> dict[str, Any]:
        customer = self._resolve_single_customer(payload.get("match", {}))
        record_id = self.repo.create_business_record(
            {
                "customer_id": customer["customer_id"],
                "record_type": payload["record_type"],
                "status": payload.get("status"),
                "record_date": normalize_date(payload.get("record_date")),
                "amount": payload.get("amount"),
                "title": payload.get("title"),
                "raw_payload": payload.get("raw_payload", {}),
            },
            _now(),
        )
        return {"record_id": record_id}

    def query_records(self, query: dict[str, Any]) -> dict[str, Any]:
        customer_id = None
        if query.get("match"):
            customer = self._resolve_single_customer(query["match"])
            customer_id = customer["customer_id"]
        records = self.repo.list_business_records(customer_id=customer_id)
        record_type = clean_text(query.get("record_type"))
        if record_type:
            records = [item for item in records if item.get("record_type") == record_type]
        if query.get("keyword"):
            keyword = clean_text(query["keyword"])
            records = [item for item in records if keyword in dumps_json(item)]
        limit = int(query.get("limit", 20))
        return {"count": len(records), "items": records[:limit]}

    def import_file(self, file_path: str | Path, *, commit: bool) -> dict[str, Any]:
        parsed = parse_file(file_path)
        warnings: list[str] = []
        preview_rows: list[dict[str, Any]] = []
        new_fields: dict[str, dict[str, Any]] = {}
        customer_count = 0
        record_count = 0
        existing_defs = self.repo.list_field_defs()

        if commit:
            self.repo.init_db()

        for table in parsed["tables"]:
            if table["mode"] == "grouped_records":
                table_result = self._import_grouped_rows(
                    table["rows"],
                    existing_defs=existing_defs,
                    commit=commit,
                )
            else:
                table_result = self._import_generic_rows(
                    table["rows"],
                    existing_defs=existing_defs,
                    commit=commit,
                )
            customer_count += table_result["customer_count"]
            record_count += table_result["record_count"]
            warnings.extend(table_result["warnings"])
            preview_rows.extend(table_result["preview_rows"])
            for item in table_result["new_fields"]:
                new_fields[item["field_key"]] = item

        preview = ImportPreview(
            mode="commit" if commit else "preview",
            source_file=str(file_path),
            customer_count=customer_count,
            record_count=record_count,
            new_field_count=len(new_fields),
            warnings=warnings,
            preview_rows=preview_rows[:20],
        )
        summary = preview.to_dict()
        job_id = self.repo.save_import_job(str(file_path), summary["mode"], summary, _now())
        return {"import_job_id": job_id, "summary": summary}

    def plan_reminders(self, payload: dict[str, Any]) -> dict[str, Any]:
        customers = self._load_customer_views()
        kinds = payload.get("kinds") or ["birthday", "due_date"]
        plans = []
        if "birthday" in kinds:
            plans.extend(build_birthday_plans(customers))
        if "due_date" in kinds:
            plans.extend(build_due_plans(customers))
        if payload.get("follow_up"):
            customer = self._resolve_single_customer(payload["follow_up"]["match"])
            plans.append(build_follow_up_plan(customer, payload["follow_up"]["date"]))
        if payload.get("schedule"):
            scheduled = schedule_plans(plans)
            return {"count": len(scheduled), "items": scheduled}
        return {"count": len(plans), "items": [asdict(plan) for plan in plans]}

    def export_customers(self, output_path: str | Path, query: dict[str, Any] | None = None) -> dict[str, Any]:
        filters = dict(query or {})
        filters["limit"] = 100000
        query_result = self.query_customers(filters)
        return export_customers(query_result["items"], output_path)

    def _import_generic_rows(
        self,
        rows: list[dict[str, str]],
        *,
        existing_defs: list[dict[str, Any]],
        commit: bool,
    ) -> dict[str, Any]:
        now = _now()
        customer_count = 0
        record_count = 0
        warnings: list[str] = []
        preview_rows: list[dict[str, Any]] = []
        created_fields: list[dict[str, Any]] = []

        columns = _collect_columns(rows)
        for column in columns:
            field_def = resolve_or_create_field(
                label=column,
                sample_values=[row.get(column) for row in rows],
                existing_defs=existing_defs,
                source="imported",
            )
            if field_def and field_def.field_key not in {item["field_key"] for item in created_fields}:
                created_fields.append(field_def.to_dict())
                existing_defs.append(field_def.to_dict())
                if commit:
                    self.repo.upsert_field_def(field_def.to_dict(), now)

        for row in rows:
            name = None
            builtin_values: dict[str, Any] = {}
            dynamic_fields: dict[str, Any] = {}
            for label, value in row.items():
                builtin_key = resolve_builtin_field(label)
                if builtin_key:
                    builtin_values[builtin_key] = value
                    if builtin_key == "name":
                        name = clean_text(value)
                else:
                    field_key = self._resolve_field_key_from_defs(label, existing_defs)
                    if field_key:
                        dynamic_fields[field_key] = value
            if not name:
                warnings.append(f"跳过一行：缺少客户姓名 -> {row}")
                continue
            preview_rows.append({"name": name, "dynamic_fields": dynamic_fields})
            customer_count += 1
            if commit:
                result = self.add_customer(
                    {
                        "name": name,
                        "primary_phone": builtin_values.get("primary_phone"),
                        "birthday": builtin_values.get("birthday"),
                        "tags": builtin_values.get("tags", ""),
                        "notes": builtin_values.get("notes"),
                        "dynamic_fields": dynamic_fields,
                    }
                )
                if not result.get("customer"):
                    warnings.append(f"导入失败：{row}")
        return {
            "customer_count": customer_count,
            "record_count": record_count,
            "warnings": warnings,
            "preview_rows": preview_rows,
            "new_fields": created_fields,
        }

    def _import_grouped_rows(
        self,
        rows: list[dict[str, str]],
        *,
        existing_defs: list[dict[str, Any]],
        commit: bool,
    ) -> dict[str, Any]:
        now = _now()
        warnings: list[str] = []
        preview_rows: list[dict[str, Any]] = []
        created_fields: list[dict[str, Any]] = []
        customer_count = 0
        record_count = 0

        for label, values in self._collect_grouped_dynamic_field_samples(rows).items():
            field_def = resolve_or_create_field(
                label=label,
                sample_values=values,
                existing_defs=existing_defs,
                source="imported",
            )
            if field_def and field_def.field_key not in {item["field_key"] for item in created_fields}:
                created_fields.append(field_def.to_dict())
                existing_defs.append(field_def.to_dict())
                if commit:
                    self.repo.upsert_field_def(field_def.to_dict(), now)

        seen_customer_ids: set[str] = set()
        preview_customer_keys: set[tuple[str, str | None, str | None]] = set()
        for row in rows:
            entities = self._extract_grouped_entities(row)
            anchor_customer_id = "__preview__"
            for entity in entities:
                preview_rows.append({"name": entity["payload"]["name"], "entity_group": entity["group"]})
                preview_customer_keys.add(
                    (
                        entity["payload"]["name"],
                        entity["payload"].get("primary_phone"),
                        entity["payload"].get("birthday"),
                    )
                )
                if commit:
                    result = self.add_customer({**entity["payload"], "dynamic_fields": entity["dynamic_fields"]})
                    customer = result["customer"]
                    customer_id = customer["customer_id"]
                    seen_customer_ids.add(customer_id)
                    if anchor_customer_id == "__preview__":
                        anchor_customer_id = customer_id

            customer_count = len(seen_customer_ids) if commit else len(preview_customer_keys)

            record_payload = self._build_grouped_record_payload(row, anchor_customer_id if entities else None)
            if record_payload:
                record_count += 1
                if commit and record_payload["customer_id"] != "__preview__":
                    self.repo.create_business_record(record_payload, now)

        return {
            "customer_count": customer_count,
            "record_count": record_count,
            "warnings": warnings,
            "preview_rows": preview_rows,
            "new_fields": created_fields,
        }

    def _build_grouped_record_payload(self, row: dict[str, str], customer_id: str | None) -> dict[str, Any] | None:
        if not customer_id:
            return None
        record_date = None
        amount = None
        title = None
        payload = {
            "customer_id": customer_id,
            "record_type": "linked_record",
            "status": "active",
            "record_date": None,
            "amount": None,
            "title": None,
            "raw_payload": {},
        }
        for label, value in row.items():
            if not clean_text(value):
                continue
            record_key = resolve_grouped_record_field(label)
            payload["raw_payload"][record_key or label] = value
            if record_key == "record_date" and not record_date:
                record_date = normalize_date(value)
            elif record_key == "amount" and not amount:
                amount = value
            elif record_key == "item_name" and not title:
                title = value
        payload["record_date"] = record_date
        payload["amount"] = amount
        payload["title"] = title or self._guess_record_title(row)
        return payload

    def _extract_grouped_entities(self, row: dict[str, str]) -> list[dict[str, Any]]:
        grouped_values: dict[str, dict[str, str]] = {}
        for label, value in row.items():
            if "_" not in label or not clean_text(value):
                continue
            group, sub_label = label.split("_", 1)
            if not clean_text(group) or not clean_text(sub_label):
                continue
            grouped_values.setdefault(group, {})[sub_label] = value

        entities: list[dict[str, Any]] = []
        for group, fields in grouped_values.items():
            builtin_values: dict[str, Any] = {}
            dynamic_fields: dict[str, Any] = {}
            name = None
            for sub_label, value in fields.items():
                builtin_key = resolve_builtin_field(sub_label)
                if builtin_key:
                    builtin_values[builtin_key] = value
                    if builtin_key == "name":
                        name = clean_text(value)
                else:
                    dynamic_fields[sub_label] = value
            if not name:
                continue
            payload = {
                "name": name,
                "primary_phone": builtin_values.get("primary_phone"),
                "birthday": builtin_values.get("birthday"),
                "notes": builtin_values.get("notes") or f"来自分组记录导入：{group}",
                "tags": [group],
            }
            entities.append({"group": group, "payload": payload, "dynamic_fields": dynamic_fields})
        return entities

    def _collect_grouped_dynamic_field_samples(self, rows: list[dict[str, str]]) -> dict[str, list[str]]:
        samples: dict[str, list[str]] = {}
        for row in rows:
            for label, value in row.items():
                if "_" not in label or not clean_text(value):
                    continue
                _, sub_label = label.split("_", 1)
                if resolve_builtin_field(sub_label):
                    continue
                samples.setdefault(sub_label, []).append(value)
        return samples

    def _guess_record_title(self, row: dict[str, str]) -> str | None:
        for label, value in row.items():
            if "_" in label:
                continue
            if not clean_text(value):
                continue
            if resolve_grouped_record_field(label) in {"record_no", "record_date", "amount", "term", "owner_name"}:
                continue
            return value
        return None

    def _save_dynamic_fields(self, entity_type: str, entity_id: str, dynamic_fields: dict[str, Any], *, source: str) -> None:
        if not dynamic_fields:
            return
        existing_defs = self.repo.list_field_defs(entity_type=entity_type)
        now = _now()
        for key_or_label, value in dynamic_fields.items():
            field_key = key_or_label
            field_def = next((item for item in existing_defs if item["field_key"] == field_key), None)
            if not field_def:
                field_def = next(
                    (
                        item
                        for item in existing_defs
                        if clean_text(item["label"]) == clean_text(key_or_label)
                        or clean_text(key_or_label) in {clean_text(alias) for alias in (item.get("aliases") or [])}
                    ),
                    None,
                )
                if field_def:
                    field_key = field_def["field_key"]
            if not field_def:
                field_def_obj = resolve_or_create_field(
                    label=key_or_label,
                    sample_values=[value],
                    existing_defs=existing_defs,
                    source=source,
                )
                if not field_def_obj:
                    continue
                field_def = field_def_obj.to_dict()
                self.repo.upsert_field_def(field_def, now)
                existing_defs.append(field_def)
                field_key = field_def["field_key"]
            normalized_value = normalize_value_for_type(value, field_def["value_type"])
            self.repo.upsert_field_value(
                entity_type=entity_type,
                entity_id=entity_id,
                field_key=field_key,
                value=clean_text(value),
                normalized_value=normalized_value,
                display_value=clean_text(value),
                now=now,
            )

    def _load_customer_views(self) -> list[dict[str, Any]]:
        customers = self.repo.list_customers()
        defs = {item["field_key"]: item for item in self.repo.list_field_defs()}
        values = self.repo.list_field_values(entity_type="customer")
        value_map: dict[str, dict[str, Any]] = {}
        for value in values:
            entity_values = value_map.setdefault(value["entity_id"], {})
            field_def = defs.get(value["field_key"], {})
            entity_values[value["field_key"]] = {
                "key": value["field_key"],
                "label": field_def.get("label", value["field_key"]),
                "type": field_def.get("value_type", "string"),
                "value": value["normalized_value"] or value["value"],
                "displayValue": value["display_value"] or value["value"],
            }
        records_by_customer: dict[str, list[dict[str, Any]]] = {}
        for record in self.repo.list_business_records():
            records_by_customer.setdefault(record["customer_id"], []).append(record)
        for customer in customers:
            customer["dynamic_field_map"] = value_map.get(customer["customer_id"], {})
            customer["dynamic_fields"] = list(customer["dynamic_field_map"].values())
            customer["records"] = records_by_customer.get(customer["customer_id"], [])
        return customers

    def _resolve_single_customer(self, match: dict[str, Any]) -> dict[str, Any]:
        if match.get("customer_id"):
            customer = self.get_customer(match["customer_id"])
            if customer:
                return customer
        query = self.query_customers({**match, "limit": 5})
        items = query["items"]
        if not items:
            raise ValueError("未找到匹配的客户")
        if len(items) > 1:
            names = "、".join(item["name"] for item in items[:5])
            raise ValueError(f"匹配到多个客户，请缩小范围：{names}")
        return items[0]

    def _resolve_field_key_from_defs(self, label: str, defs: list[dict[str, Any]]) -> str | None:
        for item in defs:
            if clean_text(label) == clean_text(item["label"]):
                return item["field_key"]
            aliases = item.get("aliases") or []
            if clean_text(label) in {clean_text(alias) for alias in aliases}:
                return item["field_key"]
        return None

    def _summarize_customer(self, customer: dict[str, Any]) -> dict[str, Any]:
        return {
            "customer_id": customer["customer_id"],
            "name": customer.get("name"),
            "primary_phone": customer.get("primary_phone"),
            "birthday": customer.get("birthday"),
            "notes": customer.get("notes"),
            "created_at": customer.get("created_at"),
            "updated_at": customer.get("updated_at"),
            "tags": customer.get("tags", []),
            "dynamic_field_map": customer.get("dynamic_field_map", {}),
            "dynamic_fields": customer.get("dynamic_fields", []),
            "record_count": len(customer.get("records", [])),
            "due_fields": customer.get("due_fields", []),
        }


def _collect_columns(rows: list[dict[str, str]]) -> list[str]:
    ordered: list[str] = []
    for row in rows:
        for key in row.keys():
            if key not in ordered:
                ordered.append(key)
    return ordered


def _now() -> str:
    return datetime.now().isoformat(timespec="seconds")
