package com.zhengmeng.ocrplatform.engine;

import com.zhengmeng.ocrplatform.common.ApiResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.URI;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

@RestController
@RequestMapping("/api/v1/engines")
public class OcrEngineController {
    private final OcrEngineRegistry engineRegistry;
    private final EngineProperties properties;
    private final OcrEngineRuntimeStateService runtimeStateService;

    public OcrEngineController(OcrEngineRegistry engineRegistry,
                               EngineProperties properties,
                               OcrEngineRuntimeStateService runtimeStateService) {
        this.engineRegistry = engineRegistry;
        this.properties = properties;
        this.runtimeStateService = runtimeStateService;
    }

    @GetMapping
    public ApiResponse<List<OcrEngineDescriptor>> listEngines() {
        List<OcrEngineDescriptor> descriptors = new ArrayList<>();
        descriptors.add(descriptor(
                "pp-chatocr",
                "PP-ChatOCR Visual + Chat",
                OcrEngineType.SEMANTIC_KV,
                Set.of("image", "pdf", "text-blocks", "tables", "semantic-key-value", "http"),
                properties.ppChatOcr().enabled()
        ));
        descriptors.add(descriptor(
                "paddle-layout",
                "PaddleOCR Layout Parsing",
                OcrEngineType.DOCUMENT_STRUCTURE,
                Set.of("image", "pdf", "text-blocks", "tables", "layout", "http"),
                properties.paddleLayout().enabled()
        ));
        return ApiResponse.ok(descriptors);
    }

    @PatchMapping("/{engineCode}/status")
    public ApiResponse<OcrEngineDescriptor> updateEngineStatus(@PathVariable String engineCode,
                                                               @RequestBody OcrEngineStatusUpdateRequest request) {
        boolean enabled = request.enabled() != null && request.enabled();
        return ApiResponse.ok(switch (engineCode) {
            case "pp-chatocr" -> {
                runtimeStateService.updateEnabled(engineCode, enabled);
                yield descriptor(
                        "pp-chatocr",
                        "PP-ChatOCR Visual + Chat",
                        OcrEngineType.SEMANTIC_KV,
                        Set.of("image", "pdf", "text-blocks", "tables", "semantic-key-value", "http"),
                        properties.ppChatOcr().enabled()
                );
            }
            case "paddle-layout" -> {
                runtimeStateService.updateEnabled(engineCode, enabled);
                yield descriptor(
                        "paddle-layout",
                        "PaddleOCR Layout Parsing",
                        OcrEngineType.DOCUMENT_STRUCTURE,
                        Set.of("image", "pdf", "text-blocks", "tables", "layout", "http"),
                        properties.paddleLayout().enabled()
                );
            }
            default -> throw new OcrEngineNotFoundException(engineCode);
        });
    }

    private OcrEngineDescriptor descriptor(String engineCode,
                                           String engineName,
                                           OcrEngineType engineType,
                                           Set<String> capabilities,
                                           boolean configured) {
        boolean registered = engineRegistry.isAvailable(engineCode);
        boolean runtimeEnabled = runtimeStateService.isEnabled(engineCode, configured);
        boolean enabled = registered && configured && runtimeEnabled;
        boolean restartRequired = configured && runtimeEnabled && !registered;
        Map<String, String> endpoints = endpoints(engineCode);
        EndpointProbe probe = probeEndpoints(endpoints);
        String message;
        if (!configured) {
            message = "启动配置未启用，需调整配置并重启后端";
        } else if (!registered) {
            message = "Adapter 未注册，需重启后端";
        } else if (!runtimeEnabled) {
            message = "已停用";
        } else {
            message = "已启用";
        }
        return new OcrEngineDescriptor(
                engineCode,
                engineName,
                engineType,
                capabilities,
                provider(engineCode),
                introduction(engineCode),
                strengths(engineCode),
                deploymentMode(engineCode),
                endpoints,
                timeoutMs(engineCode),
                probe.status(),
                probe.message(),
                probe.checkedAt(),
                registered,
                configured,
                enabled,
                restartRequired,
                message
        );
    }

    private String provider(String engineCode) {
        return switch (engineCode) {
            case "pp-chatocr", "paddle-layout" -> "PaddlePaddle / PaddleOCR 开源生态";
            default -> "未声明";
        };
    }

    private String introduction(String engineCode) {
        return switch (engineCode) {
            case "pp-chatocr" -> "面向文档语义理解和字段抽取的 OCR + Chat 组合能力，先做版面/文字视觉解析，再结合 LLM 完成指定字段问答抽取。";
            case "paddle-layout" -> "面向文档结构化解析的 PaddleOCR Layout Parsing 服务，适合输出文本块、表格 HTML、版面结构和 OCR 原始结果。";
            default -> "未声明";
        };
    }

    private List<String> strengths(String engineCode) {
        return switch (engineCode) {
            case "pp-chatocr" -> List.of("支持图片/PDF 文档理解", "可结合 MiniMax、DeepSeek 等 LLM 做字段抽取", "适合票据、合同、入职材料等语义字段回填场景");
            case "paddle-layout" -> List.of("版面结构解析能力较强", "可输出文本块与表格结构", "适合作为通用 OCR 方案中的文档结构化基础能力");
            default -> List.of();
        };
    }

    private String deploymentMode(String engineCode) {
        return switch (engineCode) {
            case "pp-chatocr" -> "外部容器服务，当前中台通过 HTTP 调用 visual/chat 两个端点";
            case "paddle-layout" -> "外部容器服务，当前中台通过 HTTP 调用 layout-parsing 端点";
            default -> "未声明";
        };
    }

    private Map<String, String> endpoints(String engineCode) {
        Map<String, String> endpoints = new LinkedHashMap<>();
        switch (engineCode) {
            case "pp-chatocr" -> {
                endpoints.put("visual", properties.ppChatOcr().visualEndpoint().toString());
                endpoints.put("chat", properties.ppChatOcr().chatEndpoint().toString());
            }
            case "paddle-layout" -> endpoints.put("layout-parsing", properties.paddleLayout().endpoint().toString());
            default -> {
            }
        }
        return endpoints;
    }

    private long timeoutMs(String engineCode) {
        return switch (engineCode) {
            case "pp-chatocr" -> properties.ppChatOcr().timeout().toMillis();
            case "paddle-layout" -> properties.paddleLayout().timeout().toMillis();
            default -> 0;
        };
    }

    private EndpointProbe probeEndpoints(Map<String, String> endpoints) {
        List<String> remoteEndpoints = endpoints.values().stream()
                .filter(value -> value != null && value.startsWith("http"))
                .toList();
        if (remoteEndpoints.isEmpty()) {
            return new EndpointProbe("本地", "无外部容器端点", LocalDateTime.now().toString());
        }
        List<String> errors = new ArrayList<>();
        for (String endpoint : remoteEndpoints) {
            URI uri = URI.create(endpoint);
            int port = uri.getPort();
            if (port < 0) {
                port = "https".equalsIgnoreCase(uri.getScheme()) ? 443 : 80;
            }
            try (Socket socket = new Socket()) {
                socket.connect(new InetSocketAddress(uri.getHost(), port), 800);
            } catch (IOException ex) {
                errors.add(endpoint + "：" + ex.getMessage());
            }
        }
        if (errors.isEmpty()) {
            return new EndpointProbe("可达", "外部容器端口可连接", LocalDateTime.now().toString());
        }
        return new EndpointProbe("不可达", String.join("；", errors), LocalDateTime.now().toString());
    }

    private record EndpointProbe(String status, String message, String checkedAt) {
    }
}
