"""Preset and signal chain data model for the Pi Multi-FX Pedal."""

from __future__ import annotations

import datetime
import hashlib
import enum
from dataclasses import dataclass, field
from typing import Optional


class Channel(enum.StrEnum):
    """Instrument/voice signal channel — each has its own independent preset bank.

    Supports guitar, bass, keys, vocals, and backing tracks. Existing preset
    files with "guitar" or "bass" channel values remain fully compatible.
    """
    GUITAR = "guitar"
    BASS = "bass"
    KEYS = "keys"
    VOCALS = "vocals"
    BACKING_TRACKS = "backing_tracks"


class FXType(enum.StrEnum):
    """Types of effects in the pedal signal chain."""
    # Existing
    NOISE_GATE = "noise_gate"
    COMPRESSOR = "compressor"
    BOOST = "boost"
    OVERDRIVE = "overdrive"
    DISTORTION = "distortion"
    FUZZ = "fuzz"
    NAM_AMP = "nam_amp"
    IR_CAB = "ir_cab"
    EQ = "eq"
    CHORUS = "chorus"
    FLANGER = "flanger"
    PHASER = "phaser"
    TREMOLO = "tremolo"
    VIBRATO = "vibrato"
    DELAY = "delay"
    REVERB = "reverb"
    VOLUME = "volume"
    TUNER = "tuner"
    # Pitch & Frequency (5)
    OCTAVER = "octaver"
    PITCH_SHIFTER = "pitch_shifter"
    HARMONIZER = "harmonizer"
    WHAMMY = "whammy"
    DETUNE = "detune"
    # Modulation (7)
    RING_MODULATOR = "ring_modulator"
    AUTO_WAH = "auto_wah"
    ENVELOPE_FILTER = "envelope_filter"
    ROTARY_SPEAKER = "rotary_speaker"
    UNI_VIBE = "uni_vibe"
    AUTO_PAN = "auto_pan"
    STEREO_WIDENER = "stereo_widener"
    # Drive & Saturation (3)
    BITCRUSHER = "bitcrusher"
    WAVEFOLDER = "wavefolder"
    RECTIFIER = "rectifier"
    # Dynamics (4)
    EXPANDER = "expander"
    DE_ESSER = "de_esser"
    TRANSIENT_SHAPER = "transient_shaper"
    SIDECHAIN_COMPRESSOR = "sidechain_compressor"
    # Filters & EQ (6)
    PARAMETRIC_EQ = "parametric_eq"
    HIGH_PASS_FILTER = "high_pass_filter"
    LOW_PASS_FILTER = "low_pass_filter"
    BAND_PASS_FILTER = "band_pass_filter"
    NOTCH_FILTER = "notch_filter"
    FORMANT_FILTER = "formant_filter"
    # Time-Based (6)
    PING_PONG_DELAY = "ping_pong_delay"
    MULTI_TAP_DELAY = "multi_tap_delay"
    REVERSE_DELAY = "reverse_delay"
    TAPE_ECHO = "tape_echo"
    SHIMMER_REVERB = "shimmer_reverb"
    LOOPER = "looper"
    # Ambience (1)
    EARLY_REFLECTIONS = "early_reflections"


@dataclass
class FXBlock:
    """A single block in the signal chain."""
    fx_type: FXType
    enabled: bool = True
    bypass: bool = False
    params: dict[str, float] = field(default_factory=dict)
    subtype: str = ""               # Algorithm variant (e.g. "hall"/"spring"/"plate" for reverb)
    nam_model_path: str = ""        # Path to .nam file (for NAM_AMP type)
    ir_file_path: str = ""          # Path to .wav IR file (for IR_CAB type)
    block_id: str = ""              # Unique instance ID within a chain (auto-filled if empty)

    def __post_init__(self) -> None:
        """Auto-generate block_id if empty."""
        if not self.block_id:
            raw = f"{self.fx_type.value}_{datetime.datetime.now(datetime.timezone.utc).timestamp()}_{id(self)}"
            self.block_id = hashlib.md5(raw.encode()).hexdigest()[:12]


@dataclass
class MIDIMapping:
    """MIDI control mapping for a parameter."""
    cc_number: int = 0
    channel: int = 0
    min_val: float = 0.0
    max_val: float = 1.0


@dataclass
class Snapshot:
    """A saved snapshot of block states and parameters within a preset."""
    name: str
    chain: list[FXBlock] = field(default_factory=list)
    master_volume: float = 0.8


@dataclass
class Preset:
    """A complete pedal preset — full signal chain state."""
    name: str
    channel: Channel = Channel.GUITAR
    bank: int = 0
    program: int = 0
    chain: list[FXBlock] = field(default_factory=list)
    midi_mappings: dict[str, MIDIMapping] = field(default_factory=dict)
    master_volume: float = 0.8
    tuner_enabled: bool = False

    # 4-Cable Method routing
    routing_mode: str = "mono"      # "mono" or "4cm"
    routing_breakpoint: int = 7     # chain index where pre/post split occurs

    # ── Snapshots (8 per preset) ──
    snapshots: dict[int, Snapshot] = field(default_factory=dict)
    """Snapshot slots 1-8. Empty dict means no snapshots saved."""


@dataclass
class Bank:
    """A bank of presets (typically 4 per bank)."""
    name: str
    number: int
    presets: list[Preset] = field(default_factory=list)