import json
from typing import Any, Mapping, Optional

from pydantic import BaseModel

from app.models.document import IngestRequest


class FileChangedEvent(BaseModel):
    event_id: str
    event_version: str
    created_at: str
    repo_id: str
    snapshot_id: str
    commit_sha: str
    file_path: str
    language: str
    file_kind: str
    change_type: str
    artifact_uri: Optional[str] = None
    old_path: Optional[str] = None


class IngestionJobEvent(BaseModel):
    run_id: str
    request: IngestRequest


def parse_file_changed_event(fields: Mapping[str, Any]) -> FileChangedEvent:
    payload = fields.get("payload")
    if payload is None:
        raise ValueError("Redis stream message is missing 'payload' field")

    if isinstance(payload, bytes):
        payload = payload.decode("utf-8")

    if not isinstance(payload, str):
        raise ValueError("Redis stream 'payload' must be a JSON string")

    try:
        return FileChangedEvent.model_validate(json.loads(payload))
    except json.JSONDecodeError as exc:
        raise ValueError("Redis stream payload is not valid JSON") from exc


def build_ingestion_job_payload(run_id: str, request: IngestRequest) -> str:
    payload = {
        "run_id": run_id,
        "request": request.model_dump(),
    }
    return json.dumps(payload, separators=(",", ":"), sort_keys=True)


def parse_ingestion_job_event(fields: Mapping[str, Any]) -> IngestionJobEvent:
    payload = fields.get("payload")
    if payload is None:
        raise ValueError("Queue message is missing 'payload' field")

    if isinstance(payload, bytes):
        payload = payload.decode("utf-8")

    if not isinstance(payload, str):
        raise ValueError("Queue 'payload' must be a JSON string")

    try:
        data = json.loads(payload)
    except json.JSONDecodeError as exc:
        raise ValueError("Queue payload is not valid JSON") from exc

    return IngestionJobEvent.model_validate(data)
