using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;

public class TTSClient
{
    private readonly string _url;
    private readonly string _text;
    private readonly string _apiKey;
    private readonly string _format;
    private readonly int _chunkLength;
    private readonly float _topP;
    private readonly float _temperature;
    private readonly int? _seed;
    private readonly int _maxNewTokens;
    private readonly float _repetitionPenalty;
    private readonly string _outputFilePath;

    private readonly List<string> _referenceTexts;
    private readonly List<byte[]> _referenceAudios;

    public TTSClient(
        string url,
        string text,
        string apiKey,
        List<string> referenceTexts,
        List<byte[]> referenceAudios,
        string format = "wav",
        int chunkLength = 0,
        float topP = 0.9f,
        float temperature = 0.9f,
        int? seed = 0,
        int maxNewTokens = 0,
        float repetitionPenalty = 1.1f,
        string outputFilePath = null
    )
    {
        _url = url ?? throw new ArgumentNullException(nameof(url));
        _text = text ?? throw new ArgumentNullException(nameof(text));
        _apiKey = apiKey ?? throw new ArgumentNullException(nameof(apiKey));
        _referenceTexts = referenceTexts ?? throw new ArgumentNullException(nameof(referenceTexts));
        _referenceAudios = referenceAudios ?? throw new ArgumentNullException(nameof(referenceAudios));
        _format = format;
        _chunkLength = chunkLength;
        _topP = topP;
        _temperature = temperature;
        _seed = seed;
        _maxNewTokens = maxNewTokens;
        _repetitionPenalty = repetitionPenalty;

        if (string.IsNullOrEmpty(outputFilePath))
        {
            _outputFilePath = Path.Combine(Application.persistentDataPath, $"output.{_format}");
        }
        else
        {
            _outputFilePath = outputFilePath;
        }
    }

    [Serializable]
    private class ReferenceObject
    {
        public string text;
        public string audio;
        public ReferenceObject(string text, string audio)
        {
            this.text = text;
            this.audio = audio;
        }
    }

    [Serializable]
    private class RequestData
    {
        public string text;
        public ReferenceObject[] references;
        public bool normalize;
        public string format;
        public int max_new_tokens;
        public int chunk_length;
        public float top_p;
        public float repetition_penalty;
        public float temperature;
        public bool streaming;
        public string use_memory_cache;
        public int? seed;
    }

    private ReferenceObject[] CreateReferenceObjects()
    {
        int count = Math.Min(_referenceTexts.Count, _referenceAudios.Count);
        ReferenceObject[] refs = new ReferenceObject[count];

        for (int i = 0; i < count; i++)
        {
            string base64Audio = (_referenceAudios[i] != null && _referenceAudios[i].Length > 0)
                ? Convert.ToBase64String(_referenceAudios[i])
                : null;
            refs[i] = new ReferenceObject(_referenceTexts[i], base64Audio);
        }
        return refs;
    }

    /// <summary>
    /// Coroutine that sends TTS request and saves audio file.
    /// </summary>
    /// <param name="resultCallback">Callback invoked on completion: (bool success, string message)</param>
    public IEnumerator SendRequestAndSaveAudioCoroutine(Action<(bool success, string message)> resultCallback)
    {
        var requestData = new RequestData()
        {
            text = _text,
            references = CreateReferenceObjects(),
            normalize = false,
            format = _format,
            max_new_tokens = _maxNewTokens,
            chunk_length = _chunkLength,
            top_p = _topP,
            repetition_penalty = _repetitionPenalty,
            temperature = _temperature,
            streaming = false,
            use_memory_cache = "on",
            seed = _seed
        };

        string json = JsonUtility.ToJson(requestData);

        byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(json);

        using var webRequest = new UnityWebRequest(_url, UnityWebRequest.kHttpVerbPOST);
        webRequest.uploadHandler = new UploadHandlerRaw(bodyRaw);
        webRequest.downloadHandler = new DownloadHandlerBuffer();
        webRequest.SetRequestHeader("Content-Type", "application/json");
        webRequest.SetRequestHeader("Authorization", "Bearer " + _apiKey);

        yield return webRequest.SendWebRequest();

#if UNITY_2020_1_OR_NEWER
        if (webRequest.result != UnityWebRequest.Result.Success)
#else
        if (webRequest.isNetworkError || webRequest.isHttpError)
#endif
        {
            string errorMsg = !string.IsNullOrEmpty(webRequest.error) ? webRequest.error : "Unknown error";
            resultCallback?.Invoke((false, $"Request error: {errorMsg}"));
            yield break;
        }

        // Save audio bytes to file
        try
        {
            byte[] audioBytes = webRequest.downloadHandler.data;
            if (audioBytes == null || audioBytes.Length == 0)
            {
                resultCallback?.Invoke((false, "Received empty audio data"));
                yield break;
            }

            File.WriteAllBytes(_outputFilePath, audioBytes);
            resultCallback?.Invoke((true, _outputFilePath));
        }
        catch (Exception e)
        {
            resultCallback?.Invoke((false, $"Failed to save audio: {e.Message}"));
        }
    }
}

make reference voice take an audioclip instead to avoid some major issues