"""Default NAM models, IRs, and directory structure for the Pi Multi-FX Pedal.

Provides helpers for:
- Getting canonical default directories for IRs and NAM models
- Generating a basic default IR WAV file
- Generating a minimal pass-through NAM model (Linear, identity)
- Ensuring all default directories and fallback files exist at startup
"""

from __future__ import annotations

import json
import logging
from pathlib import Path

import numpy as np
from scipy.io import wavfile

logger = logging.getLogger(__name__)

# ── Canonical paths ────────────────────────────────────────────────────────────

_DEFAULT_IRS_DIR = Path.home() / ".pedal" / "irs" / "default"
_DEFAULT_MODELS_DIR = Path.home() / ".pedal" / "nam" / "default"
_FACTORY_PRESET_DIR = (
    Path(__file__).resolve().parent.parent.parent / "presets" / "factory"
)


def get_default_irs_dir() -> Path:
    """Return the canonical directory for default IR files.

    This is ``~/.pedal/irs/default/``.
    """
    return _DEFAULT_IRS_DIR


def get_default_models_dir() -> Path:
    """Return the canonical directory for default NAM model files.

    This is ``~/.pedal/nam/default/``.
    """
    return _DEFAULT_MODELS_DIR


# ── Default IR generation ─────────────────────────────────────────────────────

_DEFAULT_IR_SAMPLE_RATE = 48000
_DEFAULT_IR_NUM_SAMPLES = 1024


def generate_default_ir(
    path: Path | None = None,
    sample_rate: int = _DEFAULT_IR_SAMPLE_RATE,
    num_samples: int = _DEFAULT_IR_NUM_SAMPLES,
) -> Path:
    """Generate a simple default impulse response WAV file.

    Produces a short exponentially-decaying impulse (not silence) so
    convolution will colour the sound subtly rather than mute it.

    Parameters
    ----------
    path : Path or None
        Destination path.  Defaults to ``~/.pedal/irs/default/default_ir.wav``.
    sample_rate : int
        Sample rate in Hz (default 48000).
    num_samples : int
        Length of the IR in samples (default 1024, ≈21 ms @ 48 kHz).

    Returns
    -------
    Path
        The path the WAV file was written to.
    """
    if path is None:
        path = _DEFAULT_IRS_DIR / "default_ir.wav"

    path.parent.mkdir(parents=True, exist_ok=True)

    # A short decaying impulse: 1 at sample 0, then exponential decay.
    # Using a mild decay constant so the IR is audibly non-trivial.
    t = np.arange(num_samples, dtype=np.float32)
    ir = np.exp(-t / (num_samples * 0.15))  # decay tail
    ir[0] = 1.0  # initial impulse

    # Normalise to [-1, 1]
    peak = np.max(np.abs(ir))
    if peak > 0:
        ir = ir / peak

    # Write as 16-bit PCM WAV
    wavfile.write(str(path), sample_rate, (ir * 32767).astype(np.int16))

    logger.info("Generated default IR: %s (%d samples, %d Hz)", path, num_samples, sample_rate)
    return path


# ── Pass-through NAM model generation ─────────────────────────────────────────

_NAM_PASSTHROUGH = {
    "architecture": "Linear",
    "config": {
        "receptive_field": 1,
        "bias": False,
    },
    "weights": [1.0],
}


def generate_passthrough_nam(path: Path | None = None) -> Path:
    """Generate a minimal pass-through NAM model file (Linear, identity).

    The model is a ``Conv1d(1, 1, 1)`` with weight = ``[1.0]`` and no bias.
    Loading this model in :class:`~src.dsp.nam_host.NAMHost` results in a
    transparent pass-through — audio is not modified.

    Parameters
    ----------
    path : Path or None
        Destination path.  Defaults to ``~/.pedal/nam/default/pass_through.nam``.

    Returns
    -------
    Path
        The path the .nam file was written to.
    """
    if path is None:
        path = _DEFAULT_MODELS_DIR / "pass_through.nam"

    path.parent.mkdir(parents=True, exist_ok=True)

    with open(path, "w") as f:
        json.dump(_NAM_PASSTHROUGH, f)

    logger.info("Generated pass-through NAM model: %s", path)
    return path


# ── Factory preset directories ────────────────────────────────────────────────

_BASE_DIRS = [
    Path.home() / ".pedal",
    Path.home() / ".pedal" / "presets",
    Path.home() / ".pedal" / "irs",
    Path.home() / ".pedal" / "nam",
]


def _ensure_base_dirs() -> None:
    """Create the base ``~/.pedal`` directory tree if it doesn't exist."""
    for d in _BASE_DIRS:
        d.mkdir(parents=True, exist_ok=True)


# ── Main entry point ──────────────────────────────────────────────────────────


def ensure_defaults_exist() -> bool:
    """Ensure all default directories, IRs, and NAM models exist on disk.

    Creates the following if missing:

    * ``~/.pedal/``, ``~/.pedal/presets/``, ``~/.pedal/irs/``, ``~/.pedal/nam/``
    * ``~/.pedal/irs/default/default_ir.wav`` (exponentially-decaying impulse)
    * ``~/.pedal/nam/default/pass_through.nam`` (Linear identity model)

    Returns
    -------
    bool
        ``True`` if all resources exist or were successfully created.
    """
    try:
        _ensure_base_dirs()

        # Generate default IR if it doesn't exist
        default_ir_path = _DEFAULT_IRS_DIR / "default_ir.wav"
        if not default_ir_path.exists():
            generate_default_ir(default_ir_path)
        else:
            logger.debug("Default IR already exists: %s", default_ir_path)

        # Generate pass-through NAM model if it doesn't exist
        default_nam_path = _DEFAULT_MODELS_DIR / "pass_through.nam"
        if not default_nam_path.exists():
            generate_passthrough_nam(default_nam_path)
        else:
            logger.debug("Default NAM model already exists: %s", default_nam_path)

        return True
    except Exception as exc:
        logger.warning("Failed to ensure defaults exist: %s", exc)
        return False


# ── Standalone CLI ────────────────────────────────────────────────────────────

if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO)
    ok = ensure_defaults_exist()
    print(f"Defaults {'OK' if ok else 'FAILED'}")
    print(f"  IRs dir:    {get_default_irs_dir()}")
    print(f"  Models dir: {get_default_models_dir()}")
    if ok:
        print(f"  IR file:    {'EXISTS' if (get_default_irs_dir() / 'default_ir.wav').exists() else 'MISSING'}")
        print(f"  NAM file:   {'EXISTS' if (get_default_models_dir() / 'pass_through.nam').exists() else 'MISSING'}")
