from __future__ import annotations

from datetime import UTC, datetime
from uuid import uuid4

from app.core.exceptions import DomainError
from app.modules.sync.engine import SyncEngine
from app.modules.sync.schemas import ChangeEventIn
from app.modules.zootech.catalog_apply import load_catalog_row
from app.modules.zootech.sync_content_hash import compute_content_hash


class ZootechServiceError(DomainError):
    pass


def _emit_change(enterprise_id: str, table: str, record_id: str, payload: dict, version: int, content_hash: str) -> None:
    event = ChangeEventIn(
        event_id=str(uuid4()),
        seq=None,
        domain="global",
        table=table,
        record_id=record_id,
        action="upsert",
        version=version,
        content_hash=content_hash,
        payload=payload,
        emitted_at=datetime.now(UTC),
        origin_site_id=SyncEngine.ORCHESTRATOR_SITE_ID,
    )
    SyncEngine(enterprise_id, SyncEngine.ORCHESTRATOR_SITE_ID).process_push([event], origin="orchestrator")


def upsert_component(enterprise_id: str, record_id: str | None, body: dict) -> dict:
    rid = record_id or str(uuid4())
    existing = load_catalog_row(enterprise_id, "component", rid)
    version = int(body.get("version") or (existing or {}).get("version") or 0) + 1 if existing else 1
    payload = {
        "id": rid,
        "name": body.get("name", (existing or {}).get("name", "")),
        "type": body.get("type", (existing or {}).get("type", "")),
        "is_active": body.get("is_active", (existing or {}).get("is_active", True)),
        "dry_matter": float(body.get("dry_matter", (existing or {}).get("dry_matter", 0.0))),
        "protein": float(body.get("protein", (existing or {}).get("protein", 0.0))),
        "energy": float(body.get("energy", (existing or {}).get("energy", 0.0))),
        "price": float(body.get("price", (existing or {}).get("price", 0.0))),
        "external_no": body.get("external_no", (existing or {}).get("external_no")),
        "version": version,
    }
    content_hash = compute_content_hash(payload)
    payload["content_hash"] = content_hash
    _emit_change(enterprise_id, "component", rid, payload, version, content_hash)
    return load_catalog_row(enterprise_id, "component", rid) or payload


def patch_component(enterprise_id: str, record_id: str, body: dict) -> dict:
    existing = load_catalog_row(enterprise_id, "component", record_id)
    if not existing:
        raise ZootechServiceError("NOT_FOUND")
    merged = {**existing, **body}
    return upsert_component(enterprise_id, record_id, merged)


def upsert_recipe(enterprise_id: str, record_id: str | None, body: dict) -> dict:
    rid = record_id or str(uuid4())
    existing = load_catalog_row(enterprise_id, "recipe", rid)
    version = int(body.get("version") or (existing or {}).get("version") or 0) + 1 if existing else 1
    payload = {
        "id": rid,
        "name": body.get("name", (existing or {}).get("name", "")),
        "heads_per_trip": int(body.get("heads_per_trip", (existing or {}).get("heads_per_trip", 1))),
        "mixing_time": int(body.get("mixing_time", (existing or {}).get("mixing_time", 0))),
        "trip_percent": float(body.get("trip_percent", (existing or {}).get("trip_percent", 100.0))),
        "dry_matter_locked": body.get("dry_matter_locked", (existing or {}).get("dry_matter_locked", False)),
        "version": version,
    }
    if "ingredients" in body:
        payload["ingredients"] = body["ingredients"]
    elif existing and "ingredients" in existing:
        payload["ingredients"] = existing["ingredients"]
    content_hash = compute_content_hash(payload)
    payload["content_hash"] = content_hash
    _emit_change(enterprise_id, "recipe", rid, payload, version, content_hash)
    return load_catalog_row(enterprise_id, "recipe", rid) or payload
