#!/usr/bin/env python3
"""
Test script to verify dual-mode implementation for text conditioning pairs.
Tests that both original and new modes work correctly with random selection.

Created: 2025-09-29
"""

import unittest
import os
import random

import torch
import numpy as np

import sys

sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))

from data_utils import (
    make_text_conditioning_pair,
    make_conditioning_block,
    INSTRUCTION_TEMPLATES,
)
from modules.gpt import GPTConfig
from data_types import SamplingParams


class TestDualMode(unittest.TestCase):
    """Test dual-mode implementation for text conditioning pairs."""

    @classmethod
    def setUpClass(cls):
        """Set up test fixtures."""
        cls.cfg = GPTConfig(
            semantic_codebook_size=2048,
            semantic_vocab_size=2100,
        )
        cls.tokenizer_path = "/app2/suno/data/auk_v0/tokenizer_60k.json"
        cls.has_tokenizer = os.path.exists(cls.tokenizer_path)

    def test_gpt_config_has_text_desc_token(self):
        """Test that GPTConfig has semantic_cond_audio_token."""
        self.assertIsNotNone(self.cfg.semantic_cond_audio_token)
        self.assertEqual(self.cfg.semantic_cond_audio_token, self.cfg.semantic_codebook_size + 13)
        self.assertLess(self.cfg.semantic_cond_audio_token, self.cfg.semantic_vocab_size)

    def test_instruction_template_coverage(self):
        """Test that all expected block types have instruction templates."""
        expected_types = [
            "cover",
            "artist",
            "playlist",
            "overpaint",
            "underpaint",
            "vox",
            "stem_add",
            "stem_extract",
            "stem_remove",
            "sample",
            "prefix",
            "suffix",
        ]

        for block_type in expected_types:
            with self.subTest(block_type=block_type):
                self.assertIn(block_type, INSTRUCTION_TEMPLATES)
                self.assertGreaterEqual(len(INSTRUCTION_TEMPLATES[block_type]), 5)

    @unittest.skipIf(
        not os.path.exists("/app2/suno/data/auk_v0/tokenizer_60k.json"),
        "Tokenizer not available",
    )
    def test_new_mode_text_conditioning_pairs(self):
        """Test new mode - text conditioning pairs."""
        # Create mock audio data with semantic_cond_audio_token
        mock_audio = torch.tensor(
            [self.cfg.semantic_cond_audio_token] + [100, 101, 102, 103, 104],
            dtype=torch.int64,
        )

        test_cases = [
            ("cover", None),
            ("artist", None),
            ("stem", "extract Bass"),
            ("vox", None),
            ("sample", None),
        ]

        for block_type, stem_type in test_cases:
            with self.subTest(block_type=block_type):
                pair = make_text_conditioning_pair(
                    data=mock_audio,
                    block_type=block_type,
                    is_diffusion=False,
                    tokenizer_fp=self.tokenizer_path,
                    stem_type=stem_type,
                )

                # Verify it returns a tuple
                self.assertIsInstance(pair, tuple)
                self.assertEqual(len(pair), 2)

                text_block, content_block = pair

                # Verify text block has debug text
                self.assertIsNotNone(text_block.debug_text)
                self.assertIn("[", text_block.debug_text)

                # Verify content block has data
                self.assertGreater(len(content_block), 0)

    def test_original_mode_simple_conditioning_blocks(self):
        """Test original mode - simple conditioning blocks."""
        test_cases = [
            ("cover", self.cfg.semantic_cover_token),
            ("artist", self.cfg.semantic_artist_token),
            ("stem", self.cfg.semantic_stem_token),
            ("vox", self.cfg.semantic_vox_token),
        ]

        for block_type, token_name in test_cases:
            with self.subTest(block_type=block_type):
                mock_audio = torch.tensor([token_name] + [100, 101, 102, 103, 104], dtype=torch.int64)
                block = make_conditioning_block(mock_audio, block_type, is_diffusion=False)

                # Verify block structure
                self.assertIsNotNone(block)
                self.assertEqual(block.spec.name, block_type)
                self.assertGreater(len(block), 0)

    def test_sampling_params_has_prob_text_conditioning_pairs(self):
        """Test that SamplingParams has prob_text_conditioning_pairs."""
        # Default probability
        params_default = SamplingParams()
        self.assertEqual(params_default.prob_text_conditioning_pairs, 0.5)

        # Custom probability
        params_always = SamplingParams(prob_text_conditioning_pairs=1.0)
        self.assertEqual(params_always.prob_text_conditioning_pairs, 1.0)

        params_never = SamplingParams(prob_text_conditioning_pairs=0.0)
        self.assertEqual(params_never.prob_text_conditioning_pairs, 0.0)

    def test_random_selection_distribution(self):
        """Test random selection distribution."""
        random.seed(42)

        prob = 0.5
        num_samples = 1000
        new_mode_count = sum(1 for _ in range(num_samples) if random.random() < prob)

        # Should be approximately 50% with some tolerance
        expected = num_samples * prob
        tolerance = num_samples * 0.1  # 10% tolerance
        self.assertAlmostEqual(new_mode_count, expected, delta=tolerance)


if __name__ == "__main__":
    unittest.main()
