package com.gameplatform.server.controller.link;

import com.gameplatform.server.model.dto.link.BatchDeleteByStatusRequest;
import com.gameplatform.server.model.dto.link.BatchDeleteRequest;
import com.gameplatform.server.model.dto.link.BatchDeleteResponse;
import com.gameplatform.server.model.dto.link.LinkGenerateRequest;
import com.gameplatform.server.model.dto.link.LinkGenerateResponse;
import com.gameplatform.server.model.dto.link.LinkListRequest;
import com.gameplatform.server.model.dto.link.LinkListResponse;

import com.gameplatform.server.model.dto.link.PollLoginResponse;
import com.gameplatform.server.model.dto.link.SelectRegionRequest;
import com.gameplatform.server.model.dto.link.SelectRegionResponse;
import com.gameplatform.server.model.dto.link.UserLinkStatusResponse;
import com.gameplatform.server.model.dto.link.TargetScoreResponse;
import com.gameplatform.server.service.link.LinkGenerationService;
import com.gameplatform.server.service.link.LinkListService;
import com.gameplatform.server.service.external.ScriptClient;
import com.gameplatform.server.service.link.LinkStatusService;
import io.jsonwebtoken.Claims;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import javax.validation.Valid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import reactor.core.publisher.Mono;

@RestController
@RequestMapping("/api/link")
@Tag(name = "链接管理", description = "链接生成和管理相关接口")
public class LinkController {
    private static final Logger log = LoggerFactory.getLogger(LinkController.class);

    private final LinkGenerationService linkGenerationService;
    private final LinkStatusService linkStatusService;
    private final LinkListService linkListService;
    private final ScriptClient scriptClient;

    public LinkController(LinkGenerationService linkGenerationService, 
                         LinkStatusService linkStatusService,
                         LinkListService linkListService,
                         ScriptClient scriptClient) {
        this.linkGenerationService = linkGenerationService;
        this.linkStatusService = linkStatusService;
        this.linkListService = linkListService;
        this.scriptClient = scriptClient;
    }

    @GetMapping("/list")
    @Operation(summary = "查询链接列表", description = "分页查询用户生成的链接列表，支持按状态、批次ID等条件过滤和排序")
    public Mono<LinkListResponse> getLinkList(@Valid LinkListRequest request, Authentication authentication) {
        log.info("=== 开始查询链接列表 ===");
        log.debug("请求参数: page={}, pageSize={}, status={}, batchId={}, isExpired={}, sortBy={}, sortDir={}",
                request.getPage(), request.getPageSize(), request.getStatus(),
                request.getBatchId(), request.getIsExpired(), request.getSortBy(), request.getSortDir());
        
        if (authentication == null) {
            log.error("=== 认证失败：Authentication为空 ===");
            return Mono.error(new IllegalArgumentException("用户未认证：Authentication为空"));
        }
        
        log.debug("已通过认证的请求");
        
        // 获取用户ID
        Claims claims = (Claims) authentication.getDetails();
        if (claims == null) {
            log.error("认证失败：Claims为空");
            return Mono.error(new IllegalArgumentException("用户未认证：Claims为空"));
        }
        
        Long agentId = claims.get("userId", Long.class);
        String userType = claims.get("userType", String.class);
        
        log.debug("已解析用户信息");
        
        if (agentId == null) {
            log.error("=== 无法获取用户ID ===");
            return Mono.error(new IllegalArgumentException("无法获取用户ID"));
        }
        
        return linkListService.getLinkList(agentId, request)
                .doOnSuccess(response -> {
                    log.info("链接列表查询成功: 总数={}, 当前页={}, 总页数={}", 
                            response.getTotal(), response.getPage(), response.getTotalPages());
                })
                .doOnError(error -> {
                    log.error("链接列表查询失败: agentId={}, error={}", agentId, error.getMessage(), error);
                });
    }

    @PostMapping("/generate")
    @ResponseStatus(HttpStatus.OK)
    @Operation(summary = "生成链接批次", description = "生成指定数量的链接批次。所有用户（管理员和代理商）都只能为自己生成链接，代理用户生成链接时会扣除积分，管理员生成链接时不扣除积分")
    public Mono<LinkGenerateResponse> generateLinks(@Valid @RequestBody LinkGenerateRequest request, 
                                                   Authentication authentication) {
        log.info("开始处理链接生成请求");
        log.debug("请求参数: times={}, linkCount={}", request.getTimes(), request.getLinkCount());
        
        log.debug("开始检查认证信息");
        
        if (authentication == null) {
            log.error("=== 认证失败：Authentication为空 ===");
            return Mono.error(new IllegalArgumentException("用户未认证：Authentication为空"));
        }
        
        log.debug("认证上下文可用，且已认证: {}", authentication.isAuthenticated());
        
        if (!authentication.isAuthenticated()) {
            log.error("=== 认证失败：Authentication未通过验证 ===");
            log.error("Authentication详情: {}", authentication);
            return Mono.error(new IllegalArgumentException("用户未认证：Authentication未通过验证"));
        }
        
        log.debug("认证验证通过");

        // 从认证对象中获取用户信息
        log.debug("开始解析Claims信息");
        Claims claims = (Claims) authentication.getDetails();
        if (claims == null) {
            log.error("认证失败：Claims为空");
            return Mono.error(new IllegalArgumentException("用户未认证：Claims为空"));
        }
        
        log.debug("Claims获取成功，开始解析用户信息");
        
        Long operatorId = claims.get("userId", Long.class);
        String operatorType = claims.get("userType", String.class);
        String username = claims.get("username", String.class);
        
        log.debug("用户信息已解析");

        if (operatorId == null || operatorType == null) {
            log.error("认证失败：缺少必要的用户信息");
            return Mono.error(new IllegalArgumentException("用户未认证：缺少必要的用户信息"));
        }

        log.debug("用户认证信息完整，开始处理业务逻辑");
        log.debug("业务参数: times={}, linkCount={}", request.getTimes(), request.getLinkCount());

        return linkGenerationService.generateLinks(operatorId, operatorType, 
                request.getTimes(), request.getLinkCount())
                .map(result -> {
                    log.info("链接生成成功");
                    LinkGenerateResponse response = new LinkGenerateResponse();
                    response.setBatchId(result.getBatchId());
                    response.setDeductPoints(result.getNeedPoints());
                    response.setExpireAt(result.getExpireAt());
                    response.setCodeNos(result.getCodeNos());
                    return response;
                })
                .doOnError(error -> {
                    log.error("链接生成失败: {}", error.getMessage(), error);
                });
    }

@DeleteMapping("/{codeNo}")
@Operation(summary = "删除链接", description = "删除指定的链接，用户只能删除自己创建的链接")
public Mono<Boolean> deleteLink(@PathVariable("codeNo") String codeNo, Authentication authentication) {
    log.info("=== 开始删除链接 ===");
    log.info("链接编号: {}", codeNo);

    if (authentication == null) {
        log.error("=== 认证失败：Authentication为空 ===");
        return Mono.error(new IllegalArgumentException("用户未认证：Authentication为空"));
    }

    // 获取用户ID
    Claims claims = (Claims) authentication.getDetails();
    if (claims == null) {
        log.error("=== 认证失败：Claims为空 ===");
        
        return Mono.error(new IllegalArgumentException("用户未认证：Claims为空"));
    }

    Long agentId = claims.get("userId", Long.class);
    String userType = claims.get("userType", String.class);

    log.info("用户信息: agentId={}, userType={}", agentId, userType);

    if (agentId == null) {
        log.error("=== 无法获取用户ID ===");
        return Mono.error(new IllegalArgumentException("无法获取用户ID"));
    }

    return linkStatusService.deleteLink(codeNo, agentId)
            .doOnSuccess(success -> {
                if (success) {
                    log.info("链接删除成功: codeNo={}, agentId={}", codeNo, agentId);
                } else {
                    log.warn("链接删除失败: codeNo={}, agentId={}", codeNo, agentId);
                }
            })
            .doOnError(error -> {
                log.error("删除链接时发生错误: codeNo={}, agentId={}, error={}",
                        codeNo, agentId, error.getMessage(), error);
            });
}

    @PostMapping("/{codeNo}/refund")
    @Operation(summary = "退单操作", description = "对指定链接进行退单操作，会调用外部退单接口并更新数据库状态")
    public Mono<Boolean> refundOrder(@PathVariable("codeNo") String codeNo, Authentication authentication) {
        log.info("=== 开始退单操作 ===");
        log.info("链接编号: {}", codeNo);

        if (authentication == null) {
            log.error("=== 认证失败：Authentication为空 ===");
            return Mono.error(new IllegalArgumentException("用户未认证：Authentication为空"));
        }

        // 获取用户ID
        Claims claims = (Claims) authentication.getDetails();
        if (claims == null) {
            log.error("=== 认证失败：Claims为空 ===");
            
            return Mono.error(new IllegalArgumentException("用户未认证：Claims为空"));
        }

        Long agentId = claims.get("userId", Long.class);
        String userType = claims.get("userType", String.class);

        log.info("用户信息: agentId={}, userType={}", agentId, userType);

        if (agentId == null) {
            log.error("=== 无法获取用户ID ===");
            return Mono.error(new IllegalArgumentException("无法获取用户ID"));
        }

        return linkStatusService.refundOrder(codeNo, agentId)
                .doOnSuccess(success -> {
                    if (success) {
                        log.info("退单操作成功: codeNo={}, agentId={}", codeNo, agentId);
                    } else {
                        log.warn("退单操作失败: codeNo={}, agentId={}", codeNo, agentId);
                    }
                })
                .doOnError(error -> {
                    log.error("退单操作时发生错误: codeNo={}, agentId={}, error={}",
                            codeNo, agentId, error.getMessage(), error);
                });
    }

    @PostMapping("/batch-delete-by-status")
    @Operation(summary = "按状态批量删除链接", description = "根据指定的状态批量删除链接，用户只能删除自己创建的链接，支持删除的状态：NEW、USING、LOGGED_IN、COMPLETED、REFUNDED、EXPIRED")
    public Mono<BatchDeleteResponse> batchDeleteLinksByStatus(@Valid @RequestBody BatchDeleteByStatusRequest request, Authentication authentication) {
        log.info("=== 开始按状态批量删除链接 ===");
        log.info("要删除的状态列表: {}", request.getStatusList());
        log.info("是否确认删除: {}", request.getConfirmDelete());

        if (authentication == null) {
            log.error("=== 认证失败：Authentication为空 ===");
            return Mono.error(new IllegalArgumentException("用户未认证：Authentication为空"));
        }

        // 检查是否确认删除
        if (!Boolean.TRUE.equals(request.getConfirmDelete())) {
            log.error("=== 未确认删除操作 ===");
            return Mono.error(new IllegalArgumentException("必须确认删除操作，请设置confirmDelete为true"));
        }

        // 获取用户ID
        Claims claims = (Claims) authentication.getDetails();
        if (claims == null) {
            log.error("=== 认证失败：Claims为空 ===");
            
            return Mono.error(new IllegalArgumentException("用户未认证：Claims为空"));
        }

        Long agentId = claims.get("userId", Long.class);
        String userType = claims.get("userType", String.class);

        log.info("用户信息: agentId={}, userType={}", agentId, userType);

        if (agentId == null) {
            log.error("=== 无法获取用户ID ===");
            return Mono.error(new IllegalArgumentException("无法获取用户ID"));
        }

        return linkStatusService.batchDeleteLinksByStatus(request.getStatusList(), agentId)
                .doOnSuccess(response -> {
                    log.info("按状态批量删除链接完成: 总数={}, 成功={}, 失败={}, agentId={}",
                            response.getTotalCount(), response.getSuccessCount(),
                            response.getFailedCount(), agentId);
                    if (!response.isAllSuccess()) {
                        log.warn("部分链接删除失败: 失败的链接={}, 失败原因={}",
                                response.getFailedCodeNos(), response.getFailedReasons());
                    }
                })
                .doOnError(error -> {
                    log.error("按状态批量删除链接时发生错误: agentId={}, statusList={}, error={}",
                            agentId, request.getStatusList(), error.getMessage(), error);
                });
    }

    @PostMapping("/batch-delete")
    @Operation(summary = "批量删除链接", description = "批量删除指定的链接，用户只能删除自己创建的链接，最多一次删除100个")
    public Mono<BatchDeleteResponse> batchDeleteLinks(@RequestBody BatchDeleteRequest request, Authentication authentication) {
        log.info("=== 开始批量删除链接 ===");
        log.info("要删除的链接数量: {}", request.getCodeNos().size());
        log.info("链接编号列表: {}", request.getCodeNos());

        if (authentication == null) {
            log.error("=== 认证失败：Authentication为空 ===");
            return Mono.error(new IllegalArgumentException("用户未认证：Authentication为空"));
        }

        // 获取用户ID
        Claims claims = (Claims) authentication.getDetails();
        if (claims == null) {
            log.error("=== 认证失败：Claims为空 ===");
            
            return Mono.error(new IllegalArgumentException("用户未认证：Claims为空"));
        }

        Long agentId = claims.get("userId", Long.class);
        String userType = claims.get("userType", String.class);

        log.info("用户信息: agentId={}, userType={}", agentId, userType);

        if (agentId == null) {
            log.error("=== 无法获取用户ID ===");
            return Mono.error(new IllegalArgumentException("无法获取用户ID"));
        }

        return linkStatusService.batchDeleteLinks(request.getCodeNos(), agentId)
                .doOnSuccess(response -> {
                    log.info("批量删除链接完成: 总数={}, 成功={}, 失败={}, agentId={}",
                            response.getTotalCount(), response.getSuccessCount(),
                            response.getFailedCount(), agentId);
                    if (!response.isAllSuccess()) {
                        log.warn("部分链接删除失败: 失败的链接={}, 失败原因={}",
                                response.getFailedCodeNos(), response.getFailedReasons());
                    }
                })
                .doOnError(error -> {
                    log.error("批量删除链接时发生错误: agentId={}, codeNos={}, error={}",
                            agentId, request.getCodeNos(), error.getMessage(), error);
                });
    }

    @GetMapping("/status")
    @Operation(summary = "用户端获取链接状态", description = "根据链接ID或链接编号获取链接状态，包含自动刷新逻辑，用于用户端页面")
    public Mono<UserLinkStatusResponse> getUserLinkStatus(
            @RequestParam(value = "linkId", required = false) Long linkId,
            @RequestParam(value = "codeNo", required = false) String codeNo,
            @RequestParam(value = "code", required = false) String code) {
        log.info("=== 用户端获取链接状态 ===");
        log.info("linkId: {}, codeNo: {}, code: {}", linkId, codeNo, code);
        
        // 如果提供了code参数，则将其作为codeNo使用
        String actualCodeNo = codeNo;
        if (actualCodeNo == null || actualCodeNo.trim().isEmpty()) {
            actualCodeNo = code;
        }
        
        // 验证参数：linkId和实际的codeNo至少提供一个
        if (linkId == null && (actualCodeNo == null || actualCodeNo.trim().isEmpty())) {
            log.error("参数错误：linkId、codeNo或code至少提供一个");
            return Mono.error(new IllegalArgumentException("参数错误：linkId、codeNo或code至少提供一个"));
        }
        
        return linkStatusService.getUserLinkStatus(linkId, actualCodeNo)
                .doOnSuccess(response -> {
                    log.info("用户端链接状态查询成功: status={}", response.getStatus());
                })
                .doOnError(error -> {
                    log.error("用户端链接状态查询失败: {}", error.getMessage(), error);
                });
    }


    @PostMapping("/select-region")
    @Operation(summary = "选择区域", description = "用户选择游戏区域（Q或V），选区成功后生成二维码")
    public Mono<SelectRegionResponse> selectRegion(@Valid @RequestBody SelectRegionRequest request) {
        log.info("=== 控制器: 接收选区请求 ===");
        log.info("HTTP请求参数: code={}, region={}", request.getCode(), request.getRegion());
        
        return linkStatusService.selectRegion(request.getCode(), request.getRegion())
                .doOnSuccess(response -> {
                    log.info("控制器: 选区请求处理成功");
                    log.info("响应数据: success={}, status={}, qrCodeUrl={}, qrDelaySeconds={}, machineId={}", 
                            response.isSuccess(), response.getStatus(), response.getQrCodeUrl(), 
                            response.getQrDelaySeconds(), response.getMecmachineId());
                })
                .doOnError(error -> {
                    log.error("控制器: 选区请求处理失败");
                    log.error("错误详情: code={}, region={}, errorType={}, errorMessage={}", 
                            request.getCode(), request.getRegion(), error.getClass().getSimpleName(), error.getMessage(), error);
                });
    }

    @GetMapping("/poll-login")
    @Operation(summary = "轮询上号", description = "轮询检查用户是否已上号，仅在USING状态下有效")
    public Mono<PollLoginResponse> pollLogin(@RequestParam("code") String code) {
        log.info("=== 开始轮询上号 ===");
        log.info("请求参数: code={}", code);
        
        return linkStatusService.pollLogin(code)
                .doOnSuccess(response -> {
                    log.info("轮询上号完成: success={}, status={}, view={}", 
                            response.isSuccess(), response.getStatus(), response.getView());
                })
                .doOnError(error -> {
                    log.error("轮询上号失败: code={}, error={}", 
                            code, error.getMessage(), error);
                });
    }

    /**
     * 代理二维码获取接口
     * 通过代理code获取真实设备的二维码，避免暴露设备编号
     */
    @GetMapping("/qr/{code}")
    @Operation(summary = "获取二维码", description = "通过代理code获取设备二维码，用于扫码上号")
    public Mono<ResponseEntity<byte[]>> getProxyQrCode(@PathVariable("code") String code) {
        log.info("=== 获取代理二维码 ===");
        log.info("代理code: {}", code);
        
//        // 验证代理code是否有效
//        if (!deviceCodeMappingService.isValidProxyCode(proxyCode)) {
//            log.warn("无效的代理code: {}", proxyCode);
//            return Mono.just(ResponseEntity.notFound().build());
//        }
        
        // 获取真实设备编号
        String meachainId = linkStatusService.getMechainIdByCode(code);
        if (meachainId == null) {
            log.warn("代理code对应的设备不存在: {}", code);
            return Mono.just(ResponseEntity.notFound().build());
        }
        
        log.info("代理code {} 对应设备: {}", code, meachainId);
        
        // 获取真实设备的二维码
        return scriptClient.getDeviceQrCode(meachainId)
                .map(qrData -> {
                    log.info("获取设备 {} 二维码成功，大小: {} 字节", meachainId, qrData.length);
                    
                    // 设置响应头
                    HttpHeaders headers = new HttpHeaders();
                    headers.setContentType(MediaType.IMAGE_PNG);
                    headers.setCacheControl("no-cache, no-store, must-revalidate");
                    headers.setPragma("no-cache");
                    headers.setExpires(0);
                    
                    return ResponseEntity.ok()
                            .headers(headers)
                            .body(qrData);
                })
                .onErrorResume(error -> {
                    log.error("获取设备 {} 二维码失败: {}", meachainId, error.getMessage(), error);
                    
                    // 如果是404错误，返回404；其他错误返回500
                    if (error instanceof WebClientResponseException.NotFound) {
                        log.warn("设备 {} 的二维码文件不存在，返回404", meachainId);
                        return Mono.just(ResponseEntity.status(HttpStatus.NOT_FOUND).build());
                    } else {
                        log.error("获取设备 {} 二维码时发生系统错误", meachainId);
                        return Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build());
                    }
                });
    }

    /**
     * 获取目标点数接口
     */
    @GetMapping("/target-score")
    @Operation(summary = "获取目标点数", description = "根据链接编号获取目标点数，如果返回的是数字则保存到数据库，否则提示网络繁忙")
    public Mono<TargetScoreResponse> getTargetScore(@RequestParam("codeNo") String codeNo) {
        log.info("=== 开始获取目标点数 ===");
        log.info("请求参数: codeNo={}", codeNo);
        
        return linkStatusService.getTargetScore(codeNo)
                .doOnSuccess(response -> {
                    if (response.isSuccess()) {
                        log.info("目标点数获取成功: codeNo={}, machineId={}, targetScore={}", 
                                response.getCodeNo(), response.getMachineId(), response.getCompletedPoints());
                    } else {
                        log.warn("目标点数获取失败: codeNo={}, machineId={}, error={}", 
                                response.getCodeNo(), response.getMachineId(), response.getErrorMessage());
                    }
                })
                .doOnError(error -> {
                    log.error("获取目标点数时发生异常: codeNo={}, error={}", 
                            codeNo, error.getMessage(), error);
                });
    }
}
