"""Export target handlers and factory."""

from __future__ import annotations

import json
import threading
from pathlib import Path
from typing import Protocol

import structlog

from caliper._annotation import Annotation
from caliper._config import BaseConfig, DevConfig, S3Config
from caliper._record import Record

logger = structlog.get_logger()


class ExportHandler(Protocol):
    def export(self, records: list[Record]) -> None: ...
    def export_annotations(self, annotations: list[Annotation]) -> None: ...
    def close(self) -> None: ...


def create_handler(target: str, config: BaseConfig) -> ExportHandler:
    """Factory — match on target string, return the right handler."""
    match target:
        case "dev":
            assert isinstance(config, DevConfig)
            return DevHandler(
                path=config.file_path,
                annotations_path=config.annotations_file_path,
            )
        case "s3":
            assert isinstance(config, S3Config)
            if not config.s3_bucket:
                raise ValueError("s3_bucket is required for target='s3'")
            from caliper._s3 import S3Handler

            return S3Handler(
                bucket=config.s3_bucket,
                prefix=config.s3_prefix,
                region=config.s3_region,
                access_key=config.s3_access_key,
                secret_key=config.s3_secret_key.get_secret_value() if config.s3_secret_key else None,
                endpoint=config.s3_endpoint,
                max_retries=config.max_retries,
                partition=config.s3_partition,
            )
        case _:
            raise ValueError(f"Unknown export target: {target!r}. Valid targets: 'dev', 's3'")


class DevHandler:
    """Appends records as JSONL to a local file."""

    def __init__(self, path: str = "caliper_records.jsonl", annotations_path: str | None = None) -> None:
        self._path = Path(path)
        self._annotations_path = Path(annotations_path) if annotations_path else Path("caliper_annotations.jsonl")
        self._lock = threading.Lock()

    def export(self, records: list[Record]) -> None:
        with self._lock:
            with self._path.open("a") as f:
                for record in records:
                    f.write(json.dumps(record.to_dict()) + "\n")

    def export_annotations(self, annotations: list[Annotation]) -> None:
        with self._lock:
            with self._annotations_path.open("a") as f:
                for annotation in annotations:
                    f.write(json.dumps(annotation.to_dict()) + "\n")

    def close(self) -> None:
        pass

    def get_records(self) -> list[dict]:
        """Read all records from the JSONL file. Convenience method for dev/testing."""
        if not self._path.exists():
            return []
        with self._path.open() as f:
            return [json.loads(line) for line in f if line.strip()]

    def get_annotations(self) -> list[dict]:
        """Read all annotations from the JSONL file. Convenience method for dev/testing."""
        if not self._annotations_path.exists():
            return []
        with self._annotations_path.open() as f:
            return [json.loads(line) for line in f if line.strip()]

    def clear(self) -> None:
        """Delete both JSONL files."""
        self._path.unlink(missing_ok=True)
        self._annotations_path.unlink(missing_ok=True)
