package com.yanzhu.module.ai.framework.ai.core.model.suno.api;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.StrPool;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.Map;

/**
 * Suno API
 * <p>
 * 对接 Suno Proxy：<a href="https://github.com/gcui-art/suno-api">suno-api</a>
 *
 * @author xiaoxin
 */
@Slf4j
public class SunoApi {

    private final RestTemplate restTemplate;
    private final String baseUrl;

    public SunoApi(String baseUrl) {
        this.restTemplate = new RestTemplate();
        this.baseUrl = baseUrl;
    }

    public List<MusicData> generate(MusicGenerateRequest request) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        
        HttpEntity<MusicGenerateRequest> entity = new HttpEntity<>(request, headers);
        
        try {
            ResponseEntity<List<MusicData>> response = restTemplate.exchange(
                    baseUrl + "/api/generate",
                    HttpMethod.POST,
                    entity,
                    new ParameterizedTypeReference<List<MusicData>>() {}
            );
            
            if (response.getStatusCode().series() != HttpStatus.Series.SUCCESSFUL) {
                log.error("[suno-api] 调用失败！请求参数:[{}], 响应状态码: [{}], 响应数据: [{}]", 
                        request, response.getStatusCode(), response.getBody());
                throw new IllegalStateException("[suno-api] 调用失败！");
            }
            
            // 获取响应数据
            List<MusicData> musicDataList = response.getBody();
            if (musicDataList == null) {
                return new ArrayList<>();
            }
            
            return musicDataList;
        } catch (Exception e) {
            log.error("[suno-api] 调用异常！请求参数:[{}], 异常信息: [{}]", request, e.getMessage());
            throw new RuntimeException("[suno-api] 调用失败！", e);
        }
    }

    public List<MusicData> customGenerate(MusicGenerateRequest request) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        
        HttpEntity<MusicGenerateRequest> entity = new HttpEntity<>(request, headers);
        
        try {
            ResponseEntity<List<MusicData>> response = restTemplate.exchange(
                    baseUrl + "/api/custom_generate",
                    HttpMethod.POST,
                    entity,
                    new ParameterizedTypeReference<List<MusicData>>() {}
            );
            
            if (response.getStatusCode().series() != HttpStatus.Series.SUCCESSFUL) {
                log.error("[suno-api] 调用失败！请求参数:[{}], 响应状态码: [{}], 响应数据: [{}]", 
                        request, response.getStatusCode(), response.getBody());
                throw new IllegalStateException("[suno-api] 调用失败！");
            }
            
            // 获取响应数据
            List<MusicData> musicDataList = response.getBody();
            if (musicDataList == null) {
                return new ArrayList<>();
            }
            
            return musicDataList;
        } catch (Exception e) {
            log.error("[suno-api] 调用异常！请求参数:[{}], 异常信息: [{}]", request, e.getMessage());
            throw new RuntimeException("[suno-api] 调用失败！", e);
        }
    }

    public LyricsData generateLyrics(String prompt) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        
        HttpEntity<MusicGenerateRequest> entity = new HttpEntity<>(new MusicGenerateRequest(prompt), headers);
        
        try {
            ResponseEntity<LyricsData> response = restTemplate.exchange(
                    baseUrl + "/api/generate_lyrics",
                    HttpMethod.POST,
                    entity,
                    LyricsData.class
            );
            
            if (response.getStatusCode().series() != HttpStatus.Series.SUCCESSFUL) {
                log.error("[suno-api] 调用失败！请求参数:[{}], 响应状态码: [{}], 响应数据: [{}]", 
                        prompt, response.getStatusCode(), response.getBody());
                throw new IllegalStateException("[suno-api] 调用失败！");
            }
            
            return response.getBody();
        } catch (Exception e) {
            log.error("[suno-api] 调用异常！请求参数:[{}], 异常信息: [{}]", prompt, e.getMessage());
            throw new RuntimeException("[suno-api] 调用失败！", e);
        }
    }

    public List<MusicData> getMusicList(List<String> ids) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        
        HttpEntity<String> entity = new HttpEntity<>(headers);
        
        String idsParam = CollUtil.join(ids, StrPool.COMMA);
        String url = baseUrl + "/api/get?ids=" + idsParam;
        
        try {
            ResponseEntity<List<MusicData>> response = restTemplate.exchange(
                    url,
                    HttpMethod.GET,
                    entity,
                    new ParameterizedTypeReference<List<MusicData>>() {}
            );
            
            if (response.getStatusCode().series() != HttpStatus.Series.SUCCESSFUL) {
                log.error("[suno-api] 调用失败！请求参数:[{}], 响应状态码: [{}], 响应数据: [{}]", 
                        ids, response.getStatusCode(), response.getBody());
                throw new IllegalStateException("[suno-api] 调用失败！");
            }
            
            // 获取响应数据
            List<MusicData> musicDataList = response.getBody();
            if (musicDataList == null) {
                return new ArrayList<>();
            }
            
            return musicDataList;
        } catch (Exception e) {
            log.error("[suno-api] 调用异常！请求参数:[{}], 异常信息: [{}]", ids, e.getMessage());
            throw new RuntimeException("[suno-api] 调用失败！", e);
        }
    }

    public LimitUsageData getLimitUsage() {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        
        HttpEntity<String> entity = new HttpEntity<>(headers);
        
        try {
            ResponseEntity<LimitUsageData> response = restTemplate.exchange(
                    baseUrl + "/api/get_limit",
                    HttpMethod.GET,
                    entity,
                    LimitUsageData.class
            );
            
            if (response.getStatusCode().series() != HttpStatus.Series.SUCCESSFUL) {
                log.error("[suno-api] 调用失败！请求参数:[{}], 响应状态码: [{}], 响应数据: [{}]", 
                        "getLimitUsage", response.getStatusCode(), response.getBody());
                throw new IllegalStateException("[suno-api] 调用失败！");
            }
            
            return response.getBody();
        } catch (Exception e) {
            log.error("[suno-api] 调用异常！请求参数:[{}], 异常信息: [{}]", "getLimitUsage", e.getMessage());
            throw new RuntimeException("[suno-api] 调用失败！", e);
        }
    }

    /**
     * 根据提示生成音频
     *
     * @param prompt           用于生成音乐音频的提示
     * @param tags             音乐风格
     * @param title            音乐名称
     * @param model            模型
     * @param waitAudio        false 表示后台模式，仅返回音频任务信息，需要调用 get API 获取详细的音频信息。
     *                         true 表示同步模式，API 最多等待 100s，音频生成完毕后直接返回音频链接等信息，建议在 GPT 等 agent 中使用。
     * @param makeInstrumental 指示音乐音频是否为定制，如果为 true，则从歌词生成，否则从提示生成
     */
    @JsonInclude(value = JsonInclude.Include.NON_NULL)
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class MusicGenerateRequest {
        private String prompt;
        private String tags;
        private String title;
        private String model;
        @JsonProperty("wait_audio")
        private boolean waitAudio;
        @JsonProperty("make_instrumental")
        private boolean makeInstrumental;

        public MusicGenerateRequest(String prompt) {
            this.prompt = prompt;
            this.waitAudio = false;
            this.makeInstrumental = false;
        }

        public MusicGenerateRequest(String prompt, String model, boolean makeInstrumental) {
            this.prompt = prompt;
            this.model = model;
            this.waitAudio = false;
            this.makeInstrumental = makeInstrumental;
        }

        public MusicGenerateRequest(String prompt, String model, String tags, String title) {
            this.prompt = prompt;
            this.model = model;
            this.tags = tags;
            this.title = title;
            this.waitAudio = false;
            this.makeInstrumental = false;
        }
    }

    /**
     * Suno API 响应的音频数据
     *
     * @param id                   音乐数据的 ID
     * @param title                音乐音频的标题
     * @param imageUrl             音乐音频的图片 URL
     * @param lyric                音乐音频的歌词
     * @param audioUrl             音乐音频的 URL
     * @param videoUrl             音乐视频的 URL
     * @param createdAt            音乐音频的创建时间
     * @param modelName            模型名称
     * @param status               submitted、queued、streaming、complete
     * @param gptDescriptionPrompt 描述词
     * @param prompt               生成音乐音频的提示
     * @param type                 操作类型
     * @param tags                 音乐类型标签
     * @param duration             音乐时长
     */
    @Data
    public static class MusicData {
        private String id;
        private String title;
        @JsonProperty("image_url")
        private String imageUrl;
        private String lyric;
        @JsonProperty("audio_url")
        private String audioUrl;
        @JsonProperty("video_url")
        private String videoUrl;
        @JsonProperty("created_at")
        private String createdAt;
        @JsonProperty("model_name")
        private String modelName;
        private String status;
        @JsonProperty("gpt_description_prompt")
        private String gptDescriptionPrompt;
        @JsonProperty("error_message")
        private String errorMessage;
        private String prompt;
        private String type;
        private String tags;
        private Double duration;
    }

    /**
     * Suno API 响应的歌词数据。
     *
     * @param text   歌词
     * @param title  标题
     * @param status 状态
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class LyricsData {
        private String text;
        private String title;
        private String status;
    }

    /**
     * Suno API 响应的限额数据，目前每日免费 50
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class LimitUsageData {
        @JsonProperty("credits_left")
        private Long creditsLeft;
        private String period;
        @JsonProperty("monthly_limit")
        private Long monthlyLimit;
        @JsonProperty("monthly_usage")
        private Long monthlyUsage;
    }

}
