"""Bounded, idempotent Strava summary ingestion and optional on-demand stream enrichment."""

from __future__ import annotations

import hashlib
import json
import os
import tempfile
from dataclasses import dataclass
from datetime import datetime, timezone
from pathlib import Path

from mountain_twin.activity.contracts import (
    ACTIVITY_TAXONOMY_VERSION,
    Activity,
    ActivitySourceKind,
    RawProviderRecord,
    User,
)
from mountain_twin.activity.persistence import (
    PersistentActivityIngestionService,
    PersistentIngestionRequest,
    PersistentIngestionResult,
    SqliteActivityIdentityStore,
)
from mountain_twin.activity.repository import LocalActivityRepository

from .client import StravaClient
from .normalizer import PROVIDER, normalize_activity, normalize_telemetry


class LocalRawRecordStore:
    """Protected local source storage; this directory must be ignored or outside the repository."""
    def __init__(self, root: Path): self.root = root

    def save(self, user_id: str, source_activity_id: str, payload: dict) -> RawProviderRecord:
        encoded = json.dumps(payload, sort_keys=True, separators=(",", ":")).encode()
        digest = hashlib.sha256(encoded).hexdigest()
        raw_id = f"strava:{user_id}:{source_activity_id}:{digest[:16]}"
        path = self.root / f"{raw_id}.json"
        if not path.exists():
            self.root.mkdir(parents=True, exist_ok=True)
            fd, temporary = tempfile.mkstemp(prefix=".strava-raw-", dir=self.root)
            try:
                with os.fdopen(fd, "wb") as file:
                    os.fchmod(file.fileno(), 0o600)
                    file.write(encoded)
                os.replace(temporary, path)
                os.chmod(path, 0o600)
            finally:
                if os.path.exists(temporary):
                    os.unlink(temporary)
        return RawProviderRecord(raw_id, user_id, ActivitySourceKind.PROVIDER, PROVIDER, source_activity_id, datetime.now(timezone.utc).isoformat(), str(path), digest, "strava_api_v3")


@dataclass(frozen=True)
class BackfillResult:
    fetched: int
    ingested: int
    skipped: int


class StravaIngestionService:
    def __init__(self, client: StravaClient, repository: LocalActivityRepository, raw_store: LocalRawRecordStore, user_id: str):
        self.client, self.repository, self.raw_store, self.user_id = client, repository, raw_store, user_id

    def backfill_summaries(self, *, after: int | None = None, before: int | None = None, max_pages: int = 1, per_page: int = 30) -> BackfillResult:
        summaries = self.client.list_activities(after=after, before=before, max_pages=max_pages, per_page=per_page)
        ingested = skipped = 0
        for summary in summaries:
            canonical_id = f"strava:{self.user_id}:{summary['id']}:strava_activity_normalizer_v0_1"
            if canonical_id in self.repository._activities:
                skipped += 1
                continue
            raw = self.raw_store.save(self.user_id, str(summary["id"]), summary)
            if raw.raw_record_id not in self.repository._raw_records:
                self.repository.add_raw_record(raw)
            self.repository.add_activity(normalize_activity(raw, summary))
            ingested += 1
        return BackfillResult(len(summaries), ingested, skipped)

    def enrich_streams(self, activity: Activity) -> bool:
        source_id = activity.provenance.source_references[0].source_activity_id
        raw_id = activity.provenance.normalized_from_raw_record_ids[0]
        self.repository._raw_records[raw_id]
        streams = self.client.streams(source_id)
        telemetry_raw = self.raw_store.save(self.user_id, source_id, streams)
        if telemetry_raw.raw_record_id not in self.repository._raw_records:
            self.repository.add_raw_record(telemetry_raw)
        telemetry = normalize_telemetry(telemetry_raw, activity, streams)
        if telemetry.telemetry_id not in self.repository._telemetry:
            self.repository.add_telemetry(telemetry)
        return bool(streams)


class PersistentStravaIngestionService:
    """Durably ingest one already-acquired Strava summary/detail payload into SQLite source truth."""

    def __init__(
        self,
        store: SqliteActivityIdentityStore,
        user: User,
        *,
        taxonomy_version: str = ACTIVITY_TAXONOMY_VERSION,
    ):
        self.user = user
        self.taxonomy_version = taxonomy_version
        self.ingestion = PersistentActivityIngestionService(store)

    def ingest_activity(
        self,
        payload: dict,
        *,
        fetched_at: str,
        record_kind: str = "summary",
        source_schema_version: str | None = "strava_api_v3",
    ) -> PersistentIngestionResult:
        source_activity_id = payload.get("id")
        if source_activity_id is None or not str(source_activity_id):
            raise ValueError("Strava activity payload requires an ID")
        return self.ingestion.ingest(
            PersistentIngestionRequest(
                self.user,
                ActivitySourceKind.PROVIDER,
                PROVIDER,
                str(source_activity_id),
                payload,
                record_kind,
                fetched_at,
                source_schema_version,
                normalize_activity,
                self.taxonomy_version,
            )
        )
