"""Tenant-isolated, user-confirmed supplier layout memory."""

from __future__ import annotations

import hashlib
import json
import re
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

from bridge_platform.storage.service import read_bytes, write_bytes


APP_ID = "wp_invoices"
ALLOWED_COLUMNS = ("description", "quantity", "unit_price", "tax", "line_total")
ALLOWED_NOTES = (
    "prices_include_tax", "multi_line_descriptions", "discounts_as_negative_lines",
    "receipt_reference_not_invoice_number",
)


def supplier_key(*, tax_id: str | None, name: str | None) -> str | None:
    identity = _normalize_tax_id(tax_id) or _normalize_name(name)
    return hashlib.sha256(identity.encode("utf-8")).hexdigest() if identity else None


def create_candidate(result: dict[str, Any]) -> dict[str, Any] | None:
    quality = result.get("quality") or {}
    if not quality.get("can_teach_supplier_profile"):
        return None
    supplier = result.get("supplier") or {}
    key = supplier_key(tax_id=supplier.get("tax_id"), name=supplier.get("name"))
    if not key or not supplier.get("name"):
        return None
    now = _now()
    return {
        "profile_id": f"sp_{key[:32]}", "supplier_key": key,
        "display_name": str(supplier["name"])[:160], "status": "candidate", "version": 1,
        "invoice_type": result.get("invoice_type") if result.get("invoice_type") in {"pos", "corporate"} else "unknown",
        "hints": {"invoice_number_labels": [], "reference_labels": [],
                  "item_columns": _observed_columns(result.get("items") or []), "notes": []},
        "audit": {"created_at": now, "updated_at": now,
                  "created_from_invoice_id": str(result.get("invoice_id") or ""),
                  "confirmed_by": None, "confirmed_at": None},
    }


def confirm_profile(
    *, tenant_id: str, candidate: dict[str, Any], user_id: str,
    corrections: dict[str, Any] | None = None,
) -> dict[str, Any]:
    if not tenant_id or not user_id or candidate.get("status") not in {"candidate", "active"}:
        raise ValueError("invalid_profile_confirmation")
    profile = json.loads(json.dumps(candidate))
    hints = profile["hints"]
    corrections = corrections or {}
    hints["invoice_number_labels"] = _safe_labels(corrections.get("invoice_number_labels", hints["invoice_number_labels"]))
    hints["reference_labels"] = _safe_labels(corrections.get("reference_labels", hints["reference_labels"]))
    hints["item_columns"] = _allowlist(corrections.get("item_columns", hints["item_columns"]), ALLOWED_COLUMNS)
    hints["notes"] = _allowlist(corrections.get("notes", hints["notes"]), ALLOWED_NOTES)
    now = _now()
    profile["status"] = "active"
    profile["version"] = int(profile.get("version") or 0) + 1
    profile["audit"].update({"updated_at": now, "confirmed_by": str(user_id), "confirmed_at": now})
    save_profile(tenant_id=tenant_id, profile=profile)
    return profile


def save_profile(*, tenant_id: str, profile: dict[str, Any]) -> None:
    write_bytes(tenant_id=tenant_id, app_id=APP_ID,
                relative_path=_path(profile["supplier_key"]),
                data=json.dumps(profile, ensure_ascii=False, separators=(",", ":")).encode("utf-8"))


def stage_candidate(*, tenant_id: str, result: dict[str, Any]) -> dict[str, Any] | None:
    candidate = create_candidate(result)
    if candidate is None:
        return None
    existing = load_profile(tenant_id=tenant_id, key=candidate["supplier_key"])
    if existing and existing.get("status") == "active":
        return existing
    save_profile(tenant_id=tenant_id, profile=candidate)
    return candidate


def load_profile(*, tenant_id: str, key: str) -> dict[str, Any] | None:
    try:
        profile = json.loads(read_bytes(tenant_id=tenant_id, app_id=APP_ID, relative_path=_path(key)))
    except (FileNotFoundError, json.JSONDecodeError):
        return None
    return profile if profile.get("supplier_key") == key else None


def load_active_profile(*, tenant_id: str, key: str) -> dict[str, Any] | None:
    profile = load_profile(tenant_id=tenant_id, key=key)
    return profile if profile and profile.get("status") == "active" else None


def prompt_hints(profile: dict[str, Any] | None) -> str:
    if not profile or profile.get("status") != "active":
        return ""
    hints = profile.get("hints") or {}
    safe = {"invoice_type": profile.get("invoice_type"),
            "invoice_number_labels": _safe_labels(hints.get("invoice_number_labels")),
            "reference_labels": _safe_labels(hints.get("reference_labels")),
            "item_columns": _allowlist(hints.get("item_columns"), ALLOWED_COLUMNS),
            "notes": _allowlist(hints.get("notes"), ALLOWED_NOTES)}
    return "Confirmed supplier layout hints: " + json.dumps(safe, separators=(",", ":"))


def _path(key: str) -> Path:
    if not re.fullmatch(r"[a-f0-9]{64}", str(key)):
        raise ValueError("invalid_supplier_key")
    return Path("supplier_profiles") / f"{key}.json"


def _normalize_tax_id(value): return re.sub(r"\D", "", str(value or ""))
def _normalize_name(value): return re.sub(r"[^a-z0-9]+", " ", str(value or "").lower()).strip()
def _safe_labels(values):
    labels = []
    for value in values or []:
        label = re.sub(r"[^A-Za-z0-9 #_./&()-]+", " ", str(value)).strip()[:60]
        if label:
            labels.append(label)
    return labels[:12]
def _allowlist(values, allowed): return [str(v) for v in (values or []) if str(v) in allowed][:12]
def _observed_columns(items): return [name for name in ALLOWED_COLUMNS if any(item.get(name) is not None for item in items)]
def _now(): return datetime.now(timezone.utc).isoformat()
