import json
from pathlib import Path
from typing import Any

from ..models import CollectorError, InstanceDescriptor, JobDescriptor, RunDescriptor
from ..normalizer import observation_from_signals
from .base import CollectorContext, result


class CronCollector:
    name = "cron"
    version = "0.1"

    def collect(self, context: CollectorContext):  # type: ignore[no-untyped-def]
        started = context.now
        observations = []
        errors: list[CollectorError] = []
        for definition in context.config.definitions:
            job = JobDescriptor(str(definition["job_key"]), str(definition.get("display_name", definition["job_key"])), "cron",
                                str(definition.get("job_type", "cron")), expected_interval_seconds=definition.get("expected_interval_seconds"),
                                timeout_seconds=definition.get("timeout_seconds", context.config.timeout_seconds),
                                stale_after_seconds=definition.get("stale_after_seconds"), metadata={"schedule": definition.get("schedule")})
            source = str(definition.get("source_reference", ""))
            instance = InstanceDescriptor(job, context.host, "cron", source or str(definition.get("schedule", job.job_key)),
                                          instance_name=definition.get("instance_name"), metadata={"schedule": definition.get("schedule")})
            signals: dict[str, Any] = {"source": "cron", "configured": True, "schedule": definition.get("schedule")}
            run: RunDescriptor | None = None
            if source:
                source_path = Path(source)
                try:
                    if source_path.stat().st_size > 1_000_000:
                        raise ValueError("source file exceeds configured 1 MiB limit")
                    payload = json.loads(source_path.read_text(encoding="utf-8"))
                    if not isinstance(payload, dict):
                        raise TypeError("run evidence must be a JSON object")
                    signals.update({key: payload[key] for key in ("expected_next_run_at", "last_activity_at", "reported_state", "reported_health", "exit_code", "paused") if key in payload})
                    if payload.get("last_success_at"):
                        signals["last_activity_at"] = payload["last_success_at"]
                    run = RunDescriptor(payload.get("run_id"), started_at=None, finished_at=None,
                                        exit_code=payload.get("exit_code"), error_message=payload.get("error_message")) if payload.get("run_id") else None
                except PermissionError as exc:
                    errors.append(CollectorError("permission_denied", str(exc), instance.instance_key))
                    signals["permission_denied"] = True
                except FileNotFoundError:
                    signals["missing_source_file"] = True
                except (OSError, ValueError, json.JSONDecodeError) as exc:
                    errors.append(CollectorError(type(exc).__name__, str(exc), instance.instance_key))
                    signals["unavailable"] = True
            observations.append(observation_from_signals(job, instance, run, signals, started))
        return result(self.name, self.version, started, observations, errors, {"read_only": True})
