from __future__ import annotations

import json
from pathlib import Path
from dataclasses import dataclass
from typing import Any, Dict, List, Optional

# Top-level imports for performance/simplicity
import reapy
# Mandatory configuration per python-reapy docs
reapy.configure_reaper()

# ----------------------
# Utilities & constants
# ----------------------
PACKAGE_DIR = Path(__file__).parent
SAMPLE_DIRS_FILE = PACKAGE_DIR / "sample_dirs.json"
SAMPLE_METADATA_FILE = PACKAGE_DIR / "sample_metadata.json"
GENERATED_MIDI_DIR = PACKAGE_DIR / "generated_midi"


def _load_sample_dirs() -> List[str]:
    try:
        if SAMPLE_DIRS_FILE.exists():
            with SAMPLE_DIRS_FILE.open("r", encoding="utf-8") as f:
                data = json.load(f)
                if isinstance(data, list):
                    return [d for d in data if isinstance(d, str)]
    except Exception:
        # Be forgiving; return empty on any parse/read error
        pass
    return []


def _save_sample_dirs(dirs: List[str]) -> None:
    with SAMPLE_DIRS_FILE.open("w", encoding="utf-8") as f:
        json.dump(sorted(list(set(dirs))), f, indent=2)


def _load_sample_metadata() -> Dict[str, Any]:
    """Load cached sample metadata from disk."""
    try:
        if SAMPLE_METADATA_FILE.exists():
            with SAMPLE_METADATA_FILE.open("r", encoding="utf-8") as f:
                data = json.load(f)
                if isinstance(data, dict):
                    return data
    except Exception:
        pass
    return {}


def _save_sample_metadata(metadata: Dict[str, Any]) -> None:
    """Save sample metadata cache to disk."""
    with SAMPLE_METADATA_FILE.open("w", encoding="utf-8") as f:
        json.dump(metadata, f, indent=2)


def is_cache_valid(file_path: str, cached_mtime: Optional[float]) -> bool:
    """Check if cached metadata is still valid based on file modification time."""
    if cached_mtime is None:
        return False
    try:
        current_mtime = Path(file_path).stat().st_mtime
        return abs(current_mtime - cached_mtime) < 0.001  # Small tolerance for floating point
    except Exception:
        return False


def _ensure_output_dir(subdir_name: str) -> Path:
    """Ensure an output subdirectory exists and return its path.
    
    Args:
        subdir_name: Name of the subdirectory within the package directory
    
    Returns:
        Path to the subdirectory
    """
    output_dir = PACKAGE_DIR / subdir_name
    output_dir.mkdir(parents=True, exist_ok=True)
    return output_dir


def _generate_timestamped_filename(subdir_name: str, prefix: str, extension: str) -> Path:
    """Generate a unique timestamped filename in a subdirectory.
    
    Args:
        subdir_name: Name of the subdirectory within the package directory
        prefix: Prefix for the filename
        extension: File extension (e.g., 'mid', 'wav')
    
    Returns:
        Path to the generated filename
    """
    from datetime import datetime
    output_dir = _ensure_output_dir(subdir_name)
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
    filename = f"{prefix}_{timestamp}.{extension}"
    return output_dir / filename


def _ensure_generated_midi_dir() -> Path:
    """Ensure the generated MIDI directory exists and return its path."""
    return _ensure_output_dir("generated_midi")


def _generate_midi_filename(prefix: str = "midi") -> Path:
    """Generate a unique filename for a MIDI file in the generated_midi directory."""
    return _generate_timestamped_filename("generated_midi", prefix, "mid")


@dataclass
class Note:
    start: float  # seconds
    end: float  # seconds
    pitch: int  # 0-127
    velocity: int = 100  # 1-127
    channel: int = 0  # 0-15


def _validate_audio_params(
    frequency: Optional[float] = None,
    duration: Optional[float] = None,
    amplitude: Optional[float] = None
) -> Optional[str]:
    """Validate common audio generation parameters.
    
    Args:
        frequency: Frequency in Hz (optional)
        duration: Duration in seconds (optional)
        amplitude: Amplitude 0.0-1.0 (optional)
    
    Returns:
        Error message string if validation fails, None if all valid
    """
    if frequency is not None:
        if frequency <= 0 or frequency > 20000:
            return "Frequency must be between 0 and 20000 Hz"
    
    if duration is not None:
        if duration <= 0 or duration > 300:
            return "Duration must be between 0 and 300 seconds"
    
    if amplitude is not None:
        if amplitude <= 0 or amplitude > 1.0:
            return "Amplitude must be between 0 and 1.0"
    
    return None


__all__ = [
    "PACKAGE_DIR",
    "SAMPLE_DIRS_FILE",
    "SAMPLE_METADATA_FILE",
    "GENERATED_MIDI_DIR",
    "_load_sample_dirs",
    "_save_sample_dirs",
    "_load_sample_metadata",
    "_save_sample_metadata",
    "is_cache_valid",
    "_ensure_output_dir",
    "_generate_timestamped_filename",
    "_ensure_generated_midi_dir",
    "_generate_midi_filename",
    "_validate_audio_params",
    "Note",
]
