from __future__ import annotations

import hashlib
import json
from datetime import UTC, datetime
from pathlib import Path

import pandas as pd

from psx_signal.data.storage import DatasetStore
from psx_signal.data.provider_metadata import provider_metadata


def _range(frame: pd.DataFrame, column: str = "date") -> list[str | None]:
    if frame.empty or column not in frame:
        return [None, None]
    return [str(pd.Timestamp(frame[column].min()).date()), str(pd.Timestamp(frame[column].max()).date())]


def generate_manifest(
    store: DatasetStore,
    provider: str,
    quality: dict[str, object] | None = None,
    latest_sync: dict[str, object] | None = None,
) -> dict[str, object]:
    equities = store.read(store.equities_path)
    indices = store.read(store.kse100_path)
    actions = store.read(store.corporate_actions_path)
    eq_range, index_range = _range(equities), _range(indices)
    raw_files = sum(
        path.is_file() and path.name != ".gitkeep" for path in (store.root / "raw").rglob("*")
    ) if (store.root / "raw").exists() else 0
    identity = {
        "equity_rows": len(equities), "equity_end": eq_range[1],
        "index_rows": len(indices), "index_end": index_range[1], "raw_files": raw_files,
    }
    version = hashlib.sha256(json.dumps(identity, sort_keys=True).encode()).hexdigest()[:16]
    payload: dict[str, object] = {
        "generated_at": datetime.now(UTC).isoformat(), "provider": provider, "dataset_version": version,
        "provider_metadata": provider_metadata(provider),
        "equity_start_date": eq_range[0], "equity_end_date": eq_range[1],
        "trading_sessions": int(equities["date"].nunique()) if not equities.empty else 0,
        "symbols": int(equities["symbol"].nunique()) if not equities.empty else 0,
        "rows": len(equities), "kse100_start_date": index_range[0], "kse100_end_date": index_range[1],
        "kse100_rows": len(indices), "raw_files": raw_files, "corporate_actions": len(actions),
        "validation_errors": int((quality or {}).get("error_count", 0)),
        "validation_warnings": int((quality or {}).get("warning_count", 0)),
        "validation_info": int((quality or {}).get("info_count", 0)),
        "latest_sync": latest_sync or {},
    }
    if not indices.empty:
        payload["benchmark_provider"] = str(indices["provider"].dropna().iloc[-1]) if "provider" in indices and indices["provider"].notna().any() else None
        payload["benchmark_provider_trust"] = str(indices["provider_trust"].dropna().iloc[-1]) if "provider_trust" in indices and indices["provider_trust"].notna().any() else None
        payload["benchmark_underlying_source"] = str(indices["underlying_source"].dropna().iloc[-1]) if "underlying_source" in indices and indices["underlying_source"].notna().any() else None
    target = store.root / "normalized" / "manifest.json"
    target.parent.mkdir(parents=True, exist_ok=True)
    target.write_text(json.dumps(payload, indent=2, sort_keys=True), encoding="utf-8")
    return payload
