//
//  CameraDownloadStorage.swift
//  suixinkan
//
//  Created by Codex on 2026/6/29.
//

import Foundation
import ImageIO
import UniformTypeIdentifiers

/// 相机下载文件本地存储工具，负责按账号和相册隔离目录、相对路径持久化与安全文件名处理。
enum CameraDownloadStorage {
    static let cameraDownloadsFolderName = "CameraDownloads"

    /// 相机下载文件的业务隔离作用域。
    struct Scope: Equatable {
        let accountKey: String
        let albumID: Int

        /// 初始化账号与相册维度的下载作用域。
        init(accountKey: String, albumID: Int) {
            self.accountKey = accountKey
            self.albumID = albumID
        }

        var accountDirectoryName: String {
            CameraDownloadStorage.sanitizePathComponent(accountKey, fallback: "guest")
        }

        var albumDirectoryName: String {
            "\(max(albumID, 0))"
        }
    }

    /// Documents 根目录。
    static var documentsDirectory: URL {
        FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
    }

    /// 相机照片下载根目录。
    static var downloadsDirectory: URL {
        let directory = documentsDirectory.appendingPathComponent(cameraDownloadsFolderName, isDirectory: true)
        try? FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
        return directory
    }

    /// 相机底层下载临时目录，最终业务记录不会持久化这里的路径。
    static var temporaryDownloadsDirectory: URL {
        let directory = FileManager.default.temporaryDirectory
            .appendingPathComponent(cameraDownloadsFolderName, isDirectory: true)
        try? FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
        return directory
    }

    /// 当前账号与相册的下载根目录。
    static func scopeDirectory(for scope: Scope) -> URL {
        let directory = downloadsDirectory
            .appendingPathComponent(scope.accountDirectoryName, isDirectory: true)
            .appendingPathComponent(scope.albumDirectoryName, isDirectory: true)
        try? FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
        return directory
    }

    /// 当前账号与相册的原图目录。
    static func originalsDirectory(for scope: Scope) -> URL {
        let directory = scopeDirectory(for: scope).appendingPathComponent("originals", isDirectory: true)
        try? FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
        return directory
    }

    /// 当前账号与相册的缩略图目录。
    static func thumbnailsDirectory(for scope: Scope) -> URL {
        let directory = scopeDirectory(for: scope).appendingPathComponent("thumbnails", isDirectory: true)
        try? FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
        return directory
    }

    /// 将绝对路径转为相对 Documents 的路径，如 `CameraDownloads/user_101/6603/originals/1730_DSC.JPG`。
    static func relativePath(for url: URL) -> String {
        let standardizedURL = url.standardizedFileURL
        let documentsPath = documentsDirectory.standardizedFileURL.path
        let filePath = standardizedURL.path
        if filePath == documentsPath {
            return ""
        }
        if filePath.hasPrefix(documentsPath + "/") {
            return String(filePath.dropFirst(documentsPath.count + 1))
        }
        let privateDocumentsPath = "/private" + documentsPath
        if filePath.hasPrefix(privateDocumentsPath + "/") {
            return String(filePath.dropFirst(privateDocumentsPath.count + 1))
        }
        return filePath
    }

    /// 将持久化的相对路径、绝对路径或网络地址解析为可访问 URL。
    static func resolveLocalURL(from storedPath: String) -> URL? {
        guard !storedPath.isEmpty else { return nil }

        if storedPath.hasPrefix("http://") || storedPath.hasPrefix("https://") {
            return URL(string: storedPath)
        }

        if storedPath.hasPrefix("/") {
            return URL(fileURLWithPath: storedPath)
        }

        return documentsDirectory.appendingPathComponent(storedPath)
    }

    /// 判断文件是否位于当前账号与相册的原图目录内。
    static func isInOriginalsDirectory(_ url: URL, scope: Scope) -> Bool {
        let originalsPath = originalsDirectory(for: scope).standardizedFileURL.path
        let filePath = url.standardizedFileURL.path
        return filePath.hasPrefix(originalsPath + "/") || filePath.hasPrefix("/private" + originalsPath + "/")
    }

    /// 生成当前账号与相册内带时间戳的唯一原图 URL，避免文件名冲突。
    static func uniqueLocalURL(for filename: String, scope: Scope) -> URL {
        let timestamp = Int(Date().timeIntervalSince1970)
        let safeName = sanitizeFilename(filename)
        return originalsDirectory(for: scope).appendingPathComponent("\(timestamp)_\(safeName)")
    }

    /// 生成相机底层临时下载 URL。
    static func uniqueTemporaryURL(for filename: String) -> URL {
        let timestamp = Int(Date().timeIntervalSince1970)
        let safeName = sanitizeFilename(filename)
        return temporaryDownloadsDirectory.appendingPathComponent("\(timestamp)_\(safeName)")
    }

    /// 生成当前账号与相册内带 asset ID 的唯一缩略图文件 URL。
    static func uniqueThumbnailURL(for assetID: String, scope: Scope) -> URL {
        let safeName = sanitizeFilename(assetID)
            .replacingOccurrences(of: ".", with: "_")
        return thumbnailsDirectory(for: scope).appendingPathComponent("\(safeName)_thumb.jpg")
    }

    /// 删除当前账号与相册的本地文件目录。
    static func removeScopeDirectory(for scope: Scope) {
        let directory = scopeDirectory(for: scope)
        if FileManager.default.fileExists(atPath: directory.path) {
            try? FileManager.default.removeItem(at: directory)
        }
    }

    /// 将相机 PTP 文件名转为 iOS 文件系统可安全使用的 ASCII 名称。
    static func sanitizeFilename(_ filename: String) -> String {
        let trimmed = filename
            .trimmingCharacters(in: .whitespacesAndNewlines)
            .replacingOccurrences(of: "/", with: "_")
            .replacingOccurrences(of: ":", with: "_")

        let ascii = trimmed.unicodeScalars.filter { scalar in
            scalar.isASCII && (CharacterSet.alphanumerics.contains(scalar) || scalar == "." || scalar == "_" || scalar == "-")
        }

        var name = String(String.UnicodeScalarView(ascii))
        if name.isEmpty {
            name = "DSC_\(Int(Date().timeIntervalSince1970)).JPG"
        }
        if !name.contains(".") {
            name += ".JPG"
        }
        return name
    }

    /// 将账号等动态字符串转为安全目录片段。
    static func sanitizePathComponent(_ value: String, fallback: String) -> String {
        let trimmed = value
            .trimmingCharacters(in: .whitespacesAndNewlines)
            .replacingOccurrences(of: "/", with: "_")
            .replacingOccurrences(of: ":", with: "_")

        let ascii = trimmed.unicodeScalars.filter { scalar in
            scalar.isASCII && (CharacterSet.alphanumerics.contains(scalar) || scalar == "_" || scalar == "-")
        }

        let component = String(String.UnicodeScalarView(ascii))
        return component.isEmpty ? fallback : component
    }

    /// 将本地存储路径转为可用于 UI 预览的 file URL 字符串。
    static func previewURLString(from storedPath: String) -> String {
        guard let url = resolveLocalURL(from: storedPath),
              FileManager.default.fileExists(atPath: url.path) else {
            return ""
        }
        return url.absoluteString
    }

    /// 后台读取文件大小，避免在列表刷新路径上阻塞主线程。
    static func fileSizeBytes(for url: URL?) async -> Int64 {
        guard let url else { return 0 }
        return await Task.detached(priority: .utility) {
            (try? FileManager.default.attributesOfItem(atPath: url.path)[.size] as? Int64) ?? 0
        }.value
    }
}

/// 相机本地照片缩略图生成器，负责为列表生成小尺寸预览图。
enum CameraThumbnailGenerator {
    private static let maxPixelSize: CGFloat = 160

    /// 为本地照片生成列表缩略图，成功时返回相对 Documents 的持久化路径。
    static func generateThumbnail(for imageURL: URL, assetID: String, scope: CameraDownloadStorage.Scope) async -> String? {
        await Task.detached(priority: .utility) {
            guard FileManager.default.fileExists(atPath: imageURL.path),
                  let source = CGImageSourceCreateWithURL(imageURL as CFURL, nil) else {
                return nil
            }

            let options: [CFString: Any] = [
                kCGImageSourceCreateThumbnailFromImageAlways: true,
                kCGImageSourceShouldCacheImmediately: false,
                kCGImageSourceCreateThumbnailWithTransform: true,
                kCGImageSourceThumbnailMaxPixelSize: maxPixelSize
            ]
            guard let thumbnail = CGImageSourceCreateThumbnailAtIndex(source, 0, options as CFDictionary) else {
                return nil
            }

            let destination = CameraDownloadStorage.uniqueThumbnailURL(for: assetID, scope: scope)
            if FileManager.default.fileExists(atPath: destination.path) {
                try? FileManager.default.removeItem(at: destination)
            }
            guard let imageDestination = CGImageDestinationCreateWithURL(
                destination as CFURL,
                UTType.jpeg.identifier as CFString,
                1,
                nil
            ) else {
                return nil
            }

            CGImageDestinationAddImage(imageDestination, thumbnail, [
                kCGImageDestinationLossyCompressionQuality: 0.78
            ] as CFDictionary)

            guard CGImageDestinationFinalize(imageDestination) else { return nil }
            return CameraDownloadStorage.relativePath(for: destination)
        }.value
    }
}
