"""
HubSpot snapshot comparison helpers.
"""

from __future__ import annotations

import json
import hashlib
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Dict, List, Tuple


@dataclass
class SnapshotDiff:
    meta: Dict[str, Any]
    properties_diff: Dict[str, Any]
    pipelines_diff: Dict[str, Any]
    lists_diff: Dict[str, Any]
    workflows_diff: Dict[str, Any]

    def to_dict(self) -> Dict[str, Any]:
        return {
            "meta": self.meta,
            "properties": self.properties_diff,
            "pipelines": self.pipelines_diff,
            "lists": self.lists_diff,
            "workflows": self.workflows_diff,
        }


def compare_snapshots(a_dir: Path, b_dir: Path) -> SnapshotDiff:
    a_meta = _read_json(a_dir / "meta.json")
    b_meta = _read_json(b_dir / "meta.json")

    meta = {
        "portal_match": a_meta.get("portal_id") == b_meta.get("portal_id"),
        "portal_id_a": a_meta.get("portal_id"),
        "portal_id_b": b_meta.get("portal_id"),
        "created_at_a": a_meta.get("created_at"),
        "created_at_b": b_meta.get("created_at"),
        "workflows_included_a": (a_meta.get("included") or {}).get("workflows", False),
        "workflows_included_b": (b_meta.get("included") or {}).get("workflows", False),
    }

    properties_diff = _diff_properties(a_dir / "properties", b_dir / "properties")
    pipelines_diff = _diff_pipelines(a_dir / "pipelines", b_dir / "pipelines")
    lists_diff = _diff_lists(a_dir / "lists", b_dir / "lists")
    workflows_diff = _diff_workflows(a_dir / "workflows", b_dir / "workflows")

    return SnapshotDiff(
        meta=meta,
        properties_diff=properties_diff,
        pipelines_diff=pipelines_diff,
        lists_diff=lists_diff,
        workflows_diff=workflows_diff,
    )


def format_summary(diff: SnapshotDiff) -> str:
    lines: List[str] = []
    portal = diff.meta.get("portal_id_a") or diff.meta.get("portal_id_b") or "unknown"
    lines.append(f"Portal: {portal}")
    lines.append(f"Created: A={diff.meta.get('created_at_a')} B={diff.meta.get('created_at_b')}")
    lines.append(_format_section("Properties", diff.properties_diff))
    lines.append(_format_section("Pipelines", diff.pipelines_diff))
    lines.append(_format_section("Lists", diff.lists_diff))
    if diff.workflows_diff.get("skipped"):
        lines.append("Workflows: skipped")
    else:
        lines.append(_format_section("Workflows", diff.workflows_diff))
    return "\n".join(lines)


def _format_section(title: str, section: Dict[str, Any]) -> str:
    counts = section.get("counts") or {}
    changed = section.get("changed") or []
    line = f"{title}: +{counts.get('added', 0)} -{counts.get('removed', 0)} ~{counts.get('changed', 0)}"
    if changed:
        sample = ", ".join([_format_change_item(item) for item in changed[:10]])
        line = f"{line}\n  Changed: {sample}"
    return line


def _format_change_item(item: Dict[str, Any]) -> str:
    name = item.get("name") or item.get("id") or item.get("key") or ""
    return str(name)


def _diff_properties(a_dir: Path, b_dir: Path) -> Dict[str, Any]:
    a_props = _load_property_specs(a_dir)
    b_props = _load_property_specs(b_dir)
    return _diff_named_items(a_props, b_props, key_label="property")


def _diff_pipelines(a_dir: Path, b_dir: Path) -> Dict[str, Any]:
    a_items = _load_json_files(a_dir)
    b_items = _load_json_files(b_dir)
    return _diff_named_items(a_items, b_items, key_label="pipeline")


def _diff_lists(a_dir: Path, b_dir: Path) -> Dict[str, Any]:
    a_items = _load_json_files(a_dir)
    b_items = _load_json_files(b_dir)
    return _diff_named_items(a_items, b_items, key_label="list")


def _diff_workflows(a_dir: Path, b_dir: Path) -> Dict[str, Any]:
    if not a_dir.exists() or not b_dir.exists():
        return {"skipped": True, "counts": {"added": 0, "removed": 0, "changed": 0}, "changed": []}
    a_items = _load_json_files(a_dir)
    b_items = _load_json_files(b_dir)
    return _diff_named_items(a_items, b_items, key_label="workflow")


def _diff_named_items(
    a_items: Dict[str, Dict[str, Any]],
    b_items: Dict[str, Dict[str, Any]],
    *,
    key_label: str,
) -> Dict[str, Any]:
    added = sorted([k for k in b_items if k not in a_items])
    removed = sorted([k for k in a_items if k not in b_items])
    changed: List[Dict[str, Any]] = []

    for key in sorted(set(a_items.keys()) & set(b_items.keys())):
        before = a_items[key]
        after = b_items[key]
        before_hash = _hash_payload(before)
        after_hash = _hash_payload(after)
        if before_hash != after_hash:
            changed.append(
                {
                    "id": key,
                    "name": _item_name(after) or _item_name(before),
                    "before_hash": before_hash,
                    "after_hash": after_hash,
                    "summary": _summarize_change(key_label, before, after),
                }
            )

    return {
        "counts": {"added": len(added), "removed": len(removed), "changed": len(changed)},
        "added": added,
        "removed": removed,
        "changed": changed,
    }


def _summarize_change(label: str, before: Dict[str, Any], after: Dict[str, Any]) -> Dict[str, Any]:
    summary: Dict[str, Any] = {}
    if label == "pipeline":
        summary["stages_before"] = len(before.get("stages", []) or [])
        summary["stages_after"] = len(after.get("stages", []) or [])
    if label == "list":
        summary["name_before"] = before.get("name")
        summary["name_after"] = after.get("name")
    if label == "workflow":
        summary["revision_before"] = before.get("revisionId")
        summary["revision_after"] = after.get("revisionId")
        summary["actions_before"] = len(before.get("actions", []) or [])
        summary["actions_after"] = len(after.get("actions", []) or [])
    return summary


def _load_property_specs(path: Path) -> Dict[str, Dict[str, Any]]:
    items: Dict[str, Dict[str, Any]] = {}
    if not path.exists():
        return items
    for file_path in sorted(path.glob("*.json")):
        payload = _read_json(file_path)
        objects = payload.get("objects") or {}
        for obj_name, obj_payload in objects.items():
            for prop in obj_payload.get("properties", []) or []:
                key = f"{obj_name}:{prop.get('name')}"
                payload = dict(prop)
                payload["_object_type"] = obj_name
                items[key] = payload
    return items


def _load_json_files(path: Path) -> Dict[str, Dict[str, Any]]:
    items: Dict[str, Dict[str, Any]] = {}
    if not path.exists():
        return items
    for file_path in sorted(path.glob("*.json")):
        payload = _read_json(file_path)
        key = payload.get("listId") or payload.get("pipeline_id") or payload.get("id") or file_path.stem
        items[str(key)] = payload
    return items


def _item_name(payload: Dict[str, Any]) -> str:
    return str(payload.get("name") or payload.get("label") or payload.get("id") or "")


def _read_json(path: Path) -> Dict[str, Any]:
    return json.loads(path.read_text(encoding="utf-8"))


def _hash_payload(payload: Dict[str, Any]) -> str:
    normalized = json.dumps(payload, sort_keys=True, separators=(",", ":")).encode("utf-8")
    return hashlib.sha256(normalized).hexdigest()
