import json, os, time
from fastapi import APIRouter, HTTPException, Header
from pydantic import BaseModel
from typing import Optional, List
from app.core.auth import decode_token
from app.config import settings

router = APIRouter()

DATA_FILE = os.path.join(settings.PROCESSED_DIR, "ai_presets.json")

DEFAULT_PRESETS = [
    {
        "id": "preset_epic_orchestra_intro",
        "name": "Epic Orchestra Intro (8 Bars)",
        "keywords": ["epic orchestra", "epic orchestral", "hoành tráng", "nhạc phim epic"],
        "category": "Orchestral / Film Score",
        "default_bars": 8,
        "default_bpm": 130,
        "default_scale": "C Minor",
        "system_instruction_template": "You are a professional film composer. Create a powerful, dramatic 8-bar orchestral intro. Keep the note density low (e.g. use mostly whole notes, half notes, or quarter notes) and do NOT generate dense 16th notes or complex drum rolls. This is critical to avoid output token limit timeouts. The required structure to return via the `generate_multitrack_midi` tool consists of 3 tracks: 1. Strings: plays smooth legato chord changes (one chord per 1 or 2 bars). 2. Brass Theme: plays a swelling simple melodic line in the C3-C5 range. 3. Epic Percussion: hits heavily on beats 1 and 3. Ensure the duration is precisely 8 bars (32 beats).",
        "is_user_defined": False,
        "is_favorite": False,
        "created_at": "2026-07-23T16:00:00Z"
    },
    {
        "id": "preset_pop_piano_chords",
        "name": "Pop Piano Chords (4 Bars)",
        "keywords": ["pop piano", "piano chords", "ballad piano", "hợp âm piano"],
        "category": "Pop / Ballad",
        "default_bars": 4,
        "default_bpm": 90,
        "default_scale": "C Major",
        "system_instruction_template": "You are a professional Pop Piano player. Generate a beautiful 4-bar piano chord progression (e.g. C - G - Am - F) with pleasant chord voicing and simple accompaniment. Return the MIDI notes via `generate_multitrack_midi` function on a track named 'Pop Piano'. Keep notes simple, using mostly whole/half/quarter notes. Ensure the duration of the track is precisely 4 bars (16 beats).",
        "is_user_defined": False,
        "is_favorite": False,
        "created_at": "2026-07-23T16:00:00Z"
    },
    {
        "id": "preset_cyberpunk_synth",
        "name": "Cyberpunk Synthwave (8 Bars)",
        "keywords": ["cyberpunk synth", "synthwave", "cyberpunk", "futuristic synth"],
        "category": "Electronic / Synthwave",
        "default_bars": 8,
        "default_bpm": 120,
        "default_scale": "A Minor",
        "system_instruction_template": "You are a Synthwave producer. Generate a driving 8-bar cyberpunk synth theme. Return MIDI notes via `generate_multitrack_midi` containing: 1. Synth Bass: eighth notes on pitch A1, C2, G1. 2. Synth Lead: simple melodic line in high register C4-E5. Keep notes clean and concise to ensure fast generation.",
        "is_user_defined": False,
        "is_favorite": False,
        "created_at": "2026-07-23T16:00:00Z"
    },
    {
        "id": "preset_rearrange_jazz_swing",
        "name": "Jazz Swing Variation",
        "keywords": ["jazz", "swing", "rearrange to jazz", "jazz improv"],
        "category": "Rearrange / Variation",
        "default_bars": 4,
        "default_bpm": 120,
        "default_scale": "C Major",
        "system_instruction_template": "Rearrange the original melody into a rhythmic Jazz Swing style. Apply syncopation, 7th/9th chord extensions, and off-beat rhythmic feel.",
        "is_user_defined": False,
        "is_favorite": True,
        "created_at": "2026-07-28T11:00:00Z"
    },
    {
        "id": "preset_rearrange_synth_arpeggio",
        "name": "80s Synthwave Arpeggiator",
        "keywords": ["synthwave", "arpeggio", "arp", "16th variation", "80s synth"],
        "category": "Rearrange / Variation",
        "default_bars": 4,
        "default_bpm": 120,
        "default_scale": "C Minor",
        "system_instruction_template": "Transform this sequence into a driving, shimmering 16th-note Arpeggio string in classic 80s Synthwave/Trance style.",
        "is_user_defined": False,
        "is_favorite": False,
        "created_at": "2026-07-28T11:00:00Z"
    },
    {
        "id": "preset_rearrange_harmonize",
        "name": "Chords Harmonization (4-Note)",
        "keywords": ["harmonize", "harmony", "add chord notes", "rich melody"],
        "category": "Rearrange / Variation",
        "default_bars": 4,
        "default_bpm": 120,
        "default_scale": "C Major",
        "system_instruction_template": "Add harmonizing notes to create lush 4-note chord voicing extensions beneath the lead melody.",
        "is_user_defined": False,
        "is_favorite": False,
        "created_at": "2026-07-28T11:00:00Z"
    },
    {
        "id": "preset_rearrange_cinematic_strings",
        "name": "Cinematic Strings Staccato",
        "keywords": ["cinematic", "strings", "staccato", "staccato strings"],
        "category": "Rearrange / Variation",
        "default_bars": 4,
        "default_bpm": 120,
        "default_scale": "C Minor",
        "system_instruction_template": "Rearrange the original line into a dramatic, driving Staccato rhythm for a Strings Ensemble.",
        "is_user_defined": False,
        "is_favorite": True,
        "created_at": "2026-07-28T11:00:00Z"
    }
]


class AIPresetSchema(BaseModel):
    id: str
    name: str
    keywords: List[str]
    category: str = "General"
    default_bars: int = 8
    default_bpm: int = 120
    default_scale: str = "C Major"
    system_instruction_template: str
    is_user_defined: bool = True
    is_favorite: bool = False
    created_at: Optional[str] = None


def _load_data():
    if not os.path.exists(DATA_FILE):
        return {"user_presets": {}}
    try:
        with open(DATA_FILE, "r") as f:
            return json.load(f)
    except Exception:
        return {"user_presets": {}}


def _save_data(user_presets):
    os.makedirs(os.path.dirname(DATA_FILE), exist_ok=True)
    with open(DATA_FILE, "w") as f:
        json.dump({"user_presets": user_presets}, f, indent=2)


def _get_user_id(authorization):
    if not authorization or not authorization.startswith("Bearer "):
        return "anonymous"
    token = authorization.split(" ")[1]
    payload = decode_token(token)
    if not payload:
        return "anonymous"
    return payload.get("user_id", "anonymous")


@router.get("/presets")
async def list_presets(authorization: Optional[str] = Header(None)):
    uid = _get_user_id(authorization)
    data = _load_data()
    user_presets = data.get("user_presets", {}).get(uid, [])
    merged = DEFAULT_PRESETS + user_presets
    return {"success": True, "presets": merged}


@router.post("/presets")
async def save_preset(req: AIPresetSchema, authorization: Optional[str] = Header(None)):
    uid = _get_user_id(authorization)
    if uid == "anonymous":
        raise HTTPException(status_code=401, detail="Authentication required to save presets")

    data = _load_data()
    user_presets = data.get("user_presets", {}).get(uid, [])

    if not req.created_at:
        req.created_at = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())

    existing_idx = next((i for i, p in enumerate(user_presets) if p["id"] == req.id), None)
    preset_dict = req.model_dump()

    if existing_idx is not None:
        user_presets[existing_idx] = preset_dict
    else:
        user_presets.append(preset_dict)

    if "user_presets" not in data:
        data["user_presets"] = {}
    data["user_presets"][uid] = user_presets
    _save_data(data["user_presets"])
    return {"success": True, "preset": preset_dict}


@router.delete("/presets/{preset_id}")
async def delete_preset(preset_id: str, authorization: Optional[str] = Header(None)):
    uid = _get_user_id(authorization)
    if uid == "anonymous":
        raise HTTPException(status_code=401, detail="Authentication required to delete presets")

    data = _load_data()
    user_presets = data.get("user_presets", {}).get(uid, [])
    filtered = [p for p in user_presets if p["id"] != preset_id]

    if len(filtered) == len(user_presets):
        raise HTTPException(status_code=404, detail="Preset not found")

    if "user_presets" not in data:
        data["user_presets"] = {}
    data["user_presets"][uid] = filtered
    _save_data(data["user_presets"])
    return {"success": True, "message": "Preset deleted"}
