import os
import sys

sys.path.insert(0, "/home/christian/code/christian/scripts")
import json
import glob
import math
import torch
import torchaudio
import numpy as np
import pandas as pd
import pyloudnorm as pyln

from tqdm import tqdm

from train_ear import AudioQualityModel, create_label_encoder


def download_audio(s3_filepath: str, example_id: str, tmp_dir: str):
    filename = os.path.basename(s3_filepath)
    out_filepath = os.path.join(tmp_dir, f"{example_id}-{filename}")
    # only download the file if its not already downloaded
    if not os.path.isfile(out_filepath):
        os.system(f"aws s3 cp {s3_filepath} {out_filepath} > /dev/null 2>&1")
    return out_filepath


class AudioDataset(torch.utils.data.Dataset):
    def __init__(self, filepaths, start_s=0.0, duration_s=60.0, target_sr=48000):
        self.filepaths = filepaths
        self.start_s = start_s
        self.duration_s = duration_s
        self.target_sr = target_sr

    def __len__(self):
        return len(self.filepaths)

    def __getitem__(self, idx):
        filepath = self.filepaths[idx]
        try:
            audio, sr = torchaudio.load(filepath)
            if sr != self.target_sr:
                audio = torchaudio.functional.resample(audio, sr, self.target_sr)

            # if less than 120 seconds, repeat the audio until we reach target length
            target_length = int(self.target_sr * self.duration_s)
            if audio.shape[1] < target_length:
                # Calculate how many full repeats we need
                num_repeats = math.ceil(target_length / audio.shape[1])
                # Repeat the audio that many times
                audio = audio.repeat(1, num_repeats)
                # Then crop to exact target length
                audio = audio[:, :target_length]
            else:
                # Crop to specified duration
                start_idx = 0
                end_idx = target_length
                audio = audio[:, start_idx:end_idx]

            # ensure stereo
            if audio.shape[0] == 1:
                audio = audio.repeat(2, 1)

            # Normalize
            audio = audio / audio.abs().max()

            return {"audio": audio, "filepath": filepath}
        except Exception as e:
            print(f"Error loading {filepath}: {str(e)}")
            return None


def collate_fn(batch):
    # Filter out None values from failed loads
    batch = [b for b in batch if b is not None]
    if not batch:
        return None

    return {
        "audio": torch.stack([item["audio"] for item in batch]),
        "filepath": [item["filepath"] for item in batch],
    }


if __name__ == "__main__":

    batch_size = 4
    num_workers = 32

    # load manifest
    manifest_filepath = "/app/suno/data/audio_2ch_48khz_lg/ear_train.csv"
    with open(manifest_filepath, "r") as f:
        filepaths = [line.strip() for line in f.readlines()]
    print(len(filepaths))

    # load model
    # model_filepath = (
    #    "/app/suno/christian/checkpoints/ear-v2/2024-12-20_11-26-16_s1551/last_ckpt.pt"
    # )
    model_filepath = (
        "/app/suno/christian/checkpoints/ear-v2/2025-02-14_17-09-21_s8910/last_ckpt.pt"
    )

    ckpt = torch.load(model_filepath)
    model = AudioQualityModel(**ckpt["run_config"]["model"])
    state_dict = ckpt["model"]
    new_state_dict = {}
    for key, value in state_dict.items():
        new_key = key.replace("module.", "")
        new_state_dict[new_key] = value
    model.load_state_dict(new_state_dict)
    model.eval()
    model.cuda()

    # also load corruptions config
    # corruptions = ckpt["corruptions"]
    # label_encoder = create_label_encoder(corruptions)
    # idx_to_label = {idx: label for label, idx in label_encoder.items()}

    # Create dataset and dataloader
    dataset = AudioDataset(filepaths)
    dataloader = torch.utils.data.DataLoader(
        dataset,
        batch_size=batch_size,
        num_workers=num_workers,
        collate_fn=collate_fn,
        shuffle=False,
        pin_memory=True,
    )

    results = []
    results_filepath = (
        "/home/christian/code/christian/metadata/ear_train_corruptions_v2.csv"
    )

    # Create CSV file with headers
    with open(results_filepath, "w") as f:
        f.write("filepath,score\n")

    write_every = 32
    batch_results = []
    with torch.no_grad():
        for batch in tqdm(dataloader):
            if batch is None:
                continue

            # Move batch to GPU
            audio = batch["audio"].cuda()
            filepaths = batch["filepath"]

            # Get predictions
            scores = model.get_score_batch(audio)

            # Collect results
            for i in range(len(filepaths)):
                batch_results.append((filepaths[i], scores[i].item()))

            # Write results every 100 batches
            if len(batch_results) >= write_every * batch_size:
                with open(results_filepath, "a") as f:
                    for filepath, score in batch_results:
                        f.write(f"{filepath},{score}\n")
                batch_results = []

    # Write any remaining results
    if batch_results:
        with open(results_filepath, "a") as f:
            for filepath, score in batch_results:
                f.write(f"{filepath},{score}\n")
