package com.nbport.zgwl.service;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.nbport.zgwl.exception.ServiceErrorCodeRange;
import com.nbport.zgwl.exception.ServiceException;
import com.nbport.zgwl.utils.FileStorageUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Objects;

@Service
@Slf4j
@RequiredArgsConstructor
public class LocalFileService {

    @Value("${file.local.path:${user.dir}/upload-files}")
    private String localPath;

    public String upload(MultipartFile file) {
        if (file == null || file.isEmpty()) {
            throw new ServiceException("上传文件不能为空");
        }
        try {
            Path rootPath = Paths.get(localPath).toAbsolutePath().normalize();
            Files.createDirectories(rootPath);

            String dateFolder = LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE);
            String suffix = FileStorageUtils.getFileSuffix(Objects.requireNonNullElse(file.getOriginalFilename(), ""));
            String fileName = IdUtil.simpleUUID() + suffix;
            String relativePath = dateFolder + "/" + fileName;
            Path targetPath = FileStorageUtils.resolveFilePath(rootPath, relativePath);
            Files.createDirectories(targetPath.getParent());
            file.transferTo(targetPath);
            log.info("文件上传成功，targetPath={}", targetPath);
            return relativePath;
        } catch (IOException e) {
            log.error("文件上传失败", e);
            throw new ServiceException("文件上传失败");
        }
    }

    public ResponseEntity<ByteArrayResource> download(String fileName) {
        try {
            Path rootPath = Paths.get(localPath).toAbsolutePath().normalize();
            Path filePath = FileStorageUtils.resolveFilePath(rootPath, fileName);
            if (!Files.exists(filePath) || Files.isDirectory(filePath)) {
                throw new ServiceException(ServiceErrorCodeRange.FILE_NOT_FOUND, "文件不存在");
            }
            byte[] bytes = Files.readAllBytes(filePath);
            String contentType = Files.probeContentType(filePath);
            MediaType mediaType = contentType == null ? MediaType.APPLICATION_OCTET_STREAM : MediaType.parseMediaType(contentType);

            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(mediaType);
            headers.setContentLength(bytes.length);
            headers.setContentDisposition(ContentDisposition.inline().filename(filePath.getFileName().toString()).build());
            return ResponseEntity.ok()
                    .headers(headers)
                    .body(new ByteArrayResource(bytes));
        } catch (IOException e) {
            log.error("文件下载失败", e);
            throw new ServiceException("文件下载失败");
        }
    }

    /**
     * 从外部URL下载文件并保存到本地，返回本地相对路径（如 20260603/uuid.pdf）。
     * 文件名优先从 Content-Disposition 或 URL 路径提取，否则使用兜底名。
     */
    public String downloadFromUrl(String fileUrl) {
        return downloadFromUrl(fileUrl, null);
    }

    public String downloadFromUrl(String fileUrl, String accessToken) {
        if (fileUrl == null || fileUrl.isBlank()) {
            throw new ServiceException("下载URL不能为空");
        }
        // 先尝试不带 token（签章平台下载 URL 自带 JWT 认证）
        try {
            return doDownload(fileUrl, null);
        } catch (ServiceException e) {
            // 如果 401/403，再尝试带 token
            if (accessToken != null && !accessToken.isBlank() && e.getMessage().contains("40")) {
                log.info("无token下载失败，尝试带token重试: {}", fileUrl);
                return doDownload(fileUrl, accessToken);
            }
            throw e;
        }
    }

    private String doDownload(String fileUrl, String accessToken) {
        HttpURLConnection conn = null;
        try {
            String cleanUrl = fileUrl.contains("?") ? fileUrl.substring(0, fileUrl.indexOf("?")) : fileUrl;
            URL url = new URL(cleanUrl);
            conn = openConnection(url);
            conn.setConnectTimeout(30000);
            conn.setReadTimeout(60000);
            conn.setRequestProperty("User-Agent", "Mozilla/5.0");
            if (accessToken != null && !accessToken.isBlank()) {
                conn.setRequestProperty("access_token", accessToken);
            }

            int responseCode = conn.getResponseCode();
            if (responseCode < 200 || responseCode >= 300) {
                // 读取错误响应体用于排查
                String errorBody = "";
                try (InputStream es = conn.getErrorStream()) {
                    if (es != null) {
                        errorBody = new String(es.readAllBytes(), StandardCharsets.UTF_8);
                    }
                } catch (Exception ignored) {}
                log.error("下载签章文件失败, URL={}, code={}, body={}", fileUrl, responseCode, errorBody);
                throw new ServiceException("下载签章文件失败，HTTP状态码: " + responseCode + ", 详情: " + errorBody);
            }

            String fileName = extractFileName(fileUrl, conn);
            String suffix = FileStorageUtils.getFileSuffix(fileName);

            Path rootPath = Paths.get(localPath).toAbsolutePath().normalize();
            Files.createDirectories(rootPath);
            String dateFolder = LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE);
            String storedName = IdUtil.simpleUUID() + suffix;
            String relativePath = dateFolder + "/" + storedName;
            Path targetPath = FileStorageUtils.resolveFilePath(rootPath, relativePath);
            Files.createDirectories(targetPath.getParent());

            try (InputStream is = conn.getInputStream()) {
                Files.copy(is, targetPath);
            }

            log.info("从URL下载文件成功: {} -> {}", fileUrl, targetPath);
            return relativePath;
        } catch (IOException e) {
            log.error("从URL下载文件失败: {}", fileUrl, e);
            throw new ServiceException("下载签章文件失败: " + e.getMessage());
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
    }

    private HttpURLConnection openConnection(URL url) throws IOException {
        try {
            if ("https".equalsIgnoreCase(url.getProtocol())) {
                javax.net.ssl.HttpsURLConnection httpsConn = (javax.net.ssl.HttpsURLConnection) url.openConnection();
                javax.net.ssl.SSLContext sslContext = javax.net.ssl.SSLContext.getInstance("TLS");
                sslContext.init(null, new javax.net.ssl.TrustManager[]{
                    new javax.net.ssl.X509TrustManager() {
                        public void checkClientTrusted(java.security.cert.X509Certificate[] c, String a) {}
                        public void checkServerTrusted(java.security.cert.X509Certificate[] c, String a) {}
                        public java.security.cert.X509Certificate[] getAcceptedIssuers() { return new java.security.cert.X509Certificate[0]; }
                    }
                }, new java.security.SecureRandom());
                httpsConn.setSSLSocketFactory(sslContext.getSocketFactory());
                httpsConn.setHostnameVerifier((hostname, session) -> true);
                return httpsConn;
            }
            return (HttpURLConnection) url.openConnection();
        } catch (Exception e) {
            log.warn("SSL初始化失败，使用默认连接: {}", e.getMessage());
            return (HttpURLConnection) url.openConnection();
        }
    }

    private String extractFileName(String urlStr, HttpURLConnection conn) {
        String disposition = conn.getHeaderField("Content-Disposition");
        if (disposition != null) {
            for (String part : disposition.split(";")) {
                part = part.trim();
                if (part.toLowerCase().startsWith("filename=")) {
                    String name = part.substring(part.indexOf('=') + 1).replaceAll("^[\"\']|[\"\']$", "").trim();
                    if (!name.isBlank()) return name;
                }
            }
        }
        String path = urlStr.contains("?") ? urlStr.substring(0, urlStr.indexOf('?')) : urlStr;
        String name = path.substring(path.lastIndexOf('/') + 1);
        String suffix = FileStorageUtils.getFileSuffix(name);
        // 后缀过长(>10)说明是JWT/乱码而非真实文件名, 默认pdf
        if (!name.isBlank() && suffix.length() > 0 && suffix.length() <= 10) {
            return URLDecoder.decode(name, StandardCharsets.UTF_8);
        }
        return "signed_contract.pdf";
    }

    public byte[] readFileBytes(String fileName) {
        try {
            Path rootPath = Paths.get(localPath).toAbsolutePath().normalize();
            Path filePath = FileStorageUtils.resolveFilePath(rootPath, fileName);
            if (!Files.exists(filePath) || Files.isDirectory(filePath)) {
                throw new ServiceException(ServiceErrorCodeRange.FILE_NOT_FOUND, "文件不存在");
            }
            return Files.readAllBytes(filePath);
        } catch (IOException e) {
            log.error("文件读取失败", e);
            throw new ServiceException("文件读取失败");
        }
    }

}
