package com.weather.modules.weather.filescan.controller;

import cn.hutool.core.util.StrUtil;
import com.weather.modules.weather.filescan.dto.WeatherFileScanRecordDTO;
import com.weather.modules.weather.filescan.service.WeatherFileScanRecordService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.CacheControl;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.io.File;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("filescan/file")
@Tag(name = "文件展示")
public class FileDownloadController {

    @Resource
    private WeatherFileScanRecordService weatherFileScanRecordService;

    /** 文件缓存最大有效期（小时），图片类文件变更频率极低 */
    private static final long FILE_CACHE_MAX_AGE_HOURS = 1;
    /** 文件缓存有效期（天），用于 Last-Modified 比较 */
    private static final long FILE_CACHE_MAX_AGE_DAYS = 7;

    @GetMapping("display/{id}")
    @Operation(summary = "获取展示文件")
    @RequiresPermissions("filescan:record:display")
    public ResponseEntity<org.springframework.core.io.Resource> displayFile(
            @PathVariable("id") Long id, HttpServletRequest request) {

        WeatherFileScanRecordDTO record = weatherFileScanRecordService.get(id);
        if (record == null || record.getDisplayPath() == null) {
            return ResponseEntity.notFound().build();
        }

        File file = new File(record.getDisplayPath());
        if (!file.exists()) {
            return ResponseEntity.notFound().build();
        }

        // ETag 使用文件 MD5 hash，扫描时已计算
        String eTag = "\"" + (StrUtil.isNotBlank(record.getMd5Hash()) ? record.getMd5Hash() : file.lastModified()) + "\"";
        long lastModified = file.lastModified();

        // 条件请求：If-None-Match（ETag 匹配则返回 304）
        String ifNoneMatch = request.getHeader(HttpHeaders.IF_NONE_MATCH);
        if (StrUtil.isNotBlank(ifNoneMatch) && ifNoneMatch.equals(eTag)) {
            return ResponseEntity.status(304)
                    .eTag(eTag)
                    .cacheControl(CacheControl.maxAge(FILE_CACHE_MAX_AGE_HOURS, TimeUnit.HOURS).cachePublic())
                    .build();
        }

        // 条件请求：If-Modified-Since（文件未修改则返回 304）
        long ifModifiedSince = request.getDateHeader(HttpHeaders.IF_MODIFIED_SINCE);
        if (ifModifiedSince > 0 && lastModified / 1000 <= ifModifiedSince / 1000) {
            return ResponseEntity.status(304)
                    .eTag(eTag)
                    .lastModified(lastModified)
                    .cacheControl(CacheControl.maxAge(FILE_CACHE_MAX_AGE_HOURS, TimeUnit.HOURS).cachePublic())
                    .build();
        }

        FileSystemResource resource = new FileSystemResource(file);
        String contentType = resolveContentType(record.getFileExt());

        return ResponseEntity.ok()
                .contentType(MediaType.parseMediaType(contentType))
                .header(HttpHeaders.CONTENT_DISPOSITION,
                        "inline; filename=\"" + URLEncoder.encode(record.getFileName(), StandardCharsets.UTF_8) + "\"")
                .eTag(eTag)
                .lastModified(lastModified)
                .cacheControl(CacheControl.maxAge(FILE_CACHE_MAX_AGE_HOURS, TimeUnit.HOURS)
                        .cachePublic()
                        .staleWhileRevalidate(FILE_CACHE_MAX_AGE_DAYS, TimeUnit.DAYS))
                .body(resource);
    }

    private String resolveContentType(String ext) {
        if (ext == null) return MediaType.APPLICATION_OCTET_STREAM_VALUE;
        return switch (ext.toLowerCase()) {
            case "png" -> MediaType.IMAGE_PNG_VALUE;
            case "gif" -> MediaType.IMAGE_GIF_VALUE;
            case "jpg", "jpeg" -> MediaType.IMAGE_JPEG_VALUE;
            case "svg" -> "image/svg+xml";
            case "webp" -> "image/webp";
            case "bmp" -> "image/bmp";
            case "txt" -> MediaType.TEXT_PLAIN_VALUE;
            case "json" -> MediaType.APPLICATION_JSON_VALUE;
            case "pdf" -> MediaType.APPLICATION_PDF_VALUE;
            default -> MediaType.APPLICATION_OCTET_STREAM_VALUE;
        };
    }
}
