package tech.easyflow.publicapi.controller;

import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import tech.easyflow.ai.invoke.exception.ModelInvokeException;
import tech.easyflow.ai.invoke.mapper.OpenAiProtocolMapper;
import tech.easyflow.ai.invoke.model.UnifiedChatChunk;
import tech.easyflow.ai.invoke.model.UnifiedChatRequest;
import tech.easyflow.ai.invoke.model.UnifiedChatResponse;
import tech.easyflow.ai.invoke.protocol.openai.OpenAiChatCompletionRequest;
import tech.easyflow.ai.invoke.protocol.openai.OpenAiErrorResponse;
import tech.easyflow.ai.invoke.provider.UnifiedChatChunkObserver;
import tech.easyflow.ai.invoke.service.UnifiedModelInvokeService;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.system.service.SysApiKeyService;

import javax.annotation.Resource;
import java.io.IOException;
import java.time.Duration;

@SaIgnore
@RestController
@RequestMapping("/v1")
public class PublicModelChatController {

    private static final Logger log = LoggerFactory.getLogger(PublicModelChatController.class);
    private static final long SSE_TIMEOUT = Duration.ofMinutes(10).toMillis();

    @Resource
    private SysApiKeyService sysApiKeyService;
    @Resource
    private UnifiedModelInvokeService unifiedModelInvokeService;
    @Resource
    private OpenAiProtocolMapper openAiProtocolMapper;
    @Resource
    private ObjectMapper objectMapper;

    /**
     * 统一模型调用（OpenAI Chat Completions）
     */
    @PostMapping(
            value = "/chat/completions",
            produces = {
                    MediaType.APPLICATION_JSON_VALUE,
                    MediaType.TEXT_EVENT_STREAM_VALUE
            }
    )
    public Object chatCompletions(@RequestBody String rawBody, HttpServletRequest request) {
        try {
            String apiKey = resolveApiKey(request.getHeader(HttpHeaders.AUTHORIZATION));
            sysApiKeyService.checkApikeyPermission(apiKey, request.getRequestURI());

            OpenAiChatCompletionRequest openAiRequest = openAiProtocolMapper.readRequest(rawBody);
            UnifiedChatRequest unifiedRequest = openAiProtocolMapper.toUnifiedRequest(openAiRequest);
            if (Boolean.TRUE.equals(unifiedRequest.getStream())) {
                return createStreamEmitter(unifiedRequest);
            }
            UnifiedChatResponse response = unifiedModelInvokeService.chat(unifiedRequest);
            return buildJsonResponse(openAiProtocolMapper.toOpenAiResponse(response));
        } catch (ModelInvokeException e) {
            return buildErrorResponse(e);
        } catch (BusinessException e) {
            return buildErrorResponse(mapBusinessException(e));
        } catch (Exception e) {
            log.error("chatCompletions unexpected error: {}", e.getMessage(), e);
            return buildErrorResponse(new ModelInvokeException(
                    500,
                    "统一模型调用失败: " + e.getMessage(),
                    "api_error",
                    null,
                    "internal_error"
            ));
        }
    }

    private SseEmitter createStreamEmitter(UnifiedChatRequest unifiedRequest) {
        SseEmitter emitter = new SseEmitter(SSE_TIMEOUT);
        unifiedModelInvokeService.chatStream(unifiedRequest, new UnifiedChatChunkObserver() {
            @Override
            public void onChunk(UnifiedChatChunk chunk) {
                try {
                    String payload = objectMapper.writeValueAsString(openAiProtocolMapper.toOpenAiChunk(chunk));
                    emitter.send(SseEmitter.event().data(payload));
                } catch (IOException e) {
                    emitter.completeWithError(e);
                }
            }

            @Override
            public void onComplete() {
                try {
                    emitter.send(SseEmitter.event().data("[DONE]"));
                    emitter.complete();
                } catch (IOException e) {
                    emitter.completeWithError(e);
                }
            }

            @Override
            public void onError(Throwable throwable) {
                log.error("chatCompletions stream error: {}", throwable.getMessage(), throwable);
                emitter.completeWithError(throwable);
            }
        });
        return emitter;
    }

    private ResponseEntity<String> buildJsonResponse(Object body) {
        try {
            return ResponseEntity.ok()
                    .contentType(MediaType.APPLICATION_JSON)
                    .body(objectMapper.writeValueAsString(body));
        } catch (Exception e) {
            throw new RuntimeException("序列化响应失败", e);
        }
    }

    private ResponseEntity<String> buildErrorResponse(ModelInvokeException e) {
        OpenAiErrorResponse response = new OpenAiErrorResponse();
        OpenAiErrorResponse.Error error = new OpenAiErrorResponse.Error();
        error.setMessage(e.getMessage());
        error.setType(e.getType());
        error.setParam(e.getParam());
        error.setCode(e.getCode());
        response.setError(error);
        return ResponseEntity.status(e.getStatus())
                .contentType(MediaType.APPLICATION_JSON)
                .body(writeJson(response));
    }

    private String writeJson(Object body) {
        try {
            return objectMapper.writeValueAsString(body);
        } catch (Exception ex) {
            log.error("writeJson error: {}", ex.getMessage(), ex);
            return "{\"error\":{\"message\":\"响应序列化失败\",\"type\":\"api_error\",\"code\":\"serialization_error\"}}";
        }
    }

    private ModelInvokeException mapBusinessException(BusinessException e) {
        String message = StrUtil.blankToDefault(e.getMessage(), "访问令牌校验失败");
        if (StrUtil.containsAnyIgnoreCase(message, "apikey 不存在", "apikey 已过期", "已禁用")) {
            return ModelInvokeException.unauthorized(message);
        }
        if (StrUtil.containsAnyIgnoreCase(message, "无权限", "接口不存在")) {
            return ModelInvokeException.forbidden(message);
        }
        return ModelInvokeException.badRequest(message);
    }

    private String resolveApiKey(String authorizationHeader) {
        if (StrUtil.isBlank(authorizationHeader)) {
            throw ModelInvokeException.unauthorized("Authorization 不能为空");
        }
        String trimmed = authorizationHeader.trim();
        if (StrUtil.startWithIgnoreCase(trimmed, "Bearer ")) {
            trimmed = trimmed.substring(7).trim();
        }
        if (StrUtil.isBlank(trimmed)) {
            throw ModelInvokeException.unauthorized("Authorization 无效");
        }
        return trimmed;
    }
}
