"""Global structured logging configuration for dotpromptz.

Provides a single ``configure_logging`` entry-point that wires up
**structlog** together with the Python stdlib ``logging`` module so that:

- All ``structlog.get_logger()`` calls route through stdlib handlers.
- A ``StreamHandler`` writes coloured output to *stderr*.
- A ``FileHandler`` persists every log line to a file under the
  configurable log directory.
- Library consumers can call ``configure_logging()`` once at startup;
  the CLI does it automatically.

Log directory resolution order:

1. Explicit *log_dir* argument.
2. ``DOTPROMPTZ_LOG_DIR`` environment variable.
3. ``<tempfile.gettempdir()>/dotpromptz`` (``/tmp/dotpromptz`` on Linux).

File naming uses timestamps + PID to prevent overwrites::

    dotpromptz_20260301_143022_12345.log
"""

import logging
import os
import sys
import tempfile
from datetime import datetime
from pathlib import Path

import structlog


def _resolve_log_dir(log_dir: Path | str | None = None) -> Path:
    """Determine the directory where log and output files are written.

    Args:
        log_dir: Explicit override.  When ``None``, falls back to the
            ``DOTPROMPTZ_LOG_DIR`` env-var then to a platform temp dir.

    Returns:
        Resolved ``Path`` (directory is created if it doesn't exist).
    """
    if log_dir is not None:
        resolved = Path(log_dir)
    else:
        env = os.environ.get('DOTPROMPTZ_LOG_DIR')
        resolved = Path(env) if env else Path(tempfile.gettempdir()) / 'dotpromptz'

    resolved.mkdir(parents=True, exist_ok=True)
    return resolved


def _generate_log_filename() -> str:
    """Build a unique log-file name using timestamp + PID.

    Returns:
        Filename string like ``dotpromptz_20260301_143022_12345.log``.
    """
    ts = datetime.now().strftime('%Y%m%d_%H%M%S')
    return f'dotpromptz_{ts}_{os.getpid()}.log'


def generate_build_output_filename(prompt_stem: str) -> str:
    """Build a unique build-output file name.

    Args:
        prompt_stem: The ``.prompt`` file stem (e.g. ``"explain"``).

    Returns:
        Filename string like ``build_explain_20260301_143022_12345.yaml``.
    """
    ts = datetime.now().strftime('%Y%m%d_%H%M%S')
    return f'build_{prompt_stem}_{ts}_{os.getpid()}.yaml'


def configure_logging(
    *,
    level: int | str = logging.INFO,
    log_dir: Path | str | None = None,
) -> Path:
    """Set up structlog + stdlib logging for the entire process.

    This function is **idempotent** — calling it multiple times simply
    reconfigures the root logger.

    Args:
        level: Logging level — accepts a stdlib level name (e.g.
            ``"DEBUG"``, ``"WARNING"``) or an ``int`` constant from
            the :mod:`logging` module.  Default ``logging.INFO``.
        log_dir: Explicit log directory override (see module docstring
            for resolution order).

    Returns:
        The resolved log directory ``Path``.
    """
    resolved_dir = _resolve_log_dir(log_dir)
    effective_level = logging._checkLevel(level)  # noqa: SLF001 – normalise str→int

    # ── shared pre-chain (used by ProcessorFormatter for stdlib records) ──
    shared_processors: list[structlog.types.Processor] = [
        structlog.contextvars.merge_contextvars,
        structlog.stdlib.add_log_level,
        structlog.stdlib.add_logger_name,
        structlog.processors.TimeStamper(fmt='%Y-%m-%d %H:%M:%S'),
        structlog.stdlib.ExtraAdder(),
    ]

    # ── stdlib root logger setup ─────────────────────────────────────────
    root = logging.getLogger()
    # Remove any pre-existing handlers to ensure idempotency.
    root.handlers.clear()
    root.setLevel(effective_level)

    # Console → stderr (with colours).
    console_formatter = structlog.stdlib.ProcessorFormatter(
        processors=[
            structlog.stdlib.ProcessorFormatter.remove_processors_meta,
            structlog.dev.ConsoleRenderer(pad_event_to=0),
        ],
        foreign_pre_chain=shared_processors,
    )
    console_handler = logging.StreamHandler(sys.stderr)
    console_handler.setFormatter(console_formatter)
    root.addHandler(console_handler)

    # File (no colours).
    log_file = resolved_dir / _generate_log_filename()
    file_formatter = structlog.stdlib.ProcessorFormatter(
        processors=[
            structlog.stdlib.ProcessorFormatter.remove_processors_meta,
            structlog.dev.ConsoleRenderer(pad_event_to=0, colors=False),
        ],
        foreign_pre_chain=shared_processors,
    )
    file_handler = logging.FileHandler(str(log_file), encoding='utf-8')
    file_handler.setFormatter(file_formatter)
    root.addHandler(file_handler)

    # ── structlog configuration ──────────────────────────────────────────
    structlog.configure(
        processors=[
            structlog.contextvars.merge_contextvars,
            structlog.stdlib.add_log_level,
            structlog.stdlib.add_logger_name,
            structlog.processors.TimeStamper(fmt='%Y-%m-%d %H:%M:%S'),
            structlog.stdlib.ExtraAdder(),
            structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
        ],
        logger_factory=structlog.stdlib.LoggerFactory(),
        wrapper_class=structlog.stdlib.BoundLogger,
        cache_logger_on_first_use=False,
    )

    return resolved_dir
