import Foundation

// 接收落盘管理器（对应桌面 desktop/platform/download.go）：把接收到的字节写入 app 沙盒
// Documents。小文件整文件写（saveWhole）；大文件流式 begin / append / finalize；abort
// 丢弃临时文件。线程：由 EngineController 在主线程调用，文件 IO 同步执行（首版求简单可
// 验，后续可挪后台队列）。
final class DownloadManager
{
    private struct Active
    {
        let handle: FileHandle
        let tmpURL: URL
    }

    private var active: [String: Active] = [:]

    private func downloadDir() -> URL
    {
        return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
    }

    // 列出已落盘的接收文件（Documents 顶层，排除流式临时 .part / 隐藏文件），最近的在前。
    // 供应用内「收到的文件」视图浏览 / 打开 / 转发，也是 Files app 可见的同一批文件。
    func receivedFiles() -> [URL]
    {
        let urls = (try? FileManager.default.contentsOfDirectory(
            at: downloadDir(),
            includingPropertiesForKeys: [ .contentModificationDateKey ],
            options: [ .skipsHiddenFiles ])) ?? []
        return urls
            .filter { $0.pathExtension != "part" && !$0.lastPathComponent.hasPrefix(".cdrop-") }
            .sorted { a, b in modDate(a) > modDate(b) }
    }

    func deleteReceivedFile(_ url: URL)
    {
        try? FileManager.default.removeItem(at: url)
    }

    private func modDate(_ url: URL) -> Date
    {
        return (try? url.resourceValues(forKeys: [ .contentModificationDateKey ]))?
            .contentModificationDate ?? .distantPast
    }

    // 整文件写入（接收端小文件路径，对应桥 saveDownload）。返回最终绝对路径。
    func saveWhole(name: String, data: Data) throws -> String
    {
        let url = uniqueURL(for: sanitize(name))
        try data.write(to: url)
        return url.path
    }

    func begin(sessionId: String) throws
    {
        cleanup(sessionId)   // re-begin 安全：先清旧条目再开新文件
        let tmp = downloadDir().appendingPathComponent(".cdrop-\(sanitize(sessionId)).part")
        FileManager.default.createFile(atPath: tmp.path, contents: nil)
        let handle = try FileHandle(forWritingTo: tmp)
        active[sessionId] = Active(handle: handle, tmpURL: tmp)
    }

    func append(sessionId: String, data: Data) throws
    {
        guard let a = active[sessionId] else { throw Err.unknownSession }
        try a.handle.write(contentsOf: data)
    }

    func finalize(sessionId: String, name: String) throws -> String
    {
        guard let a = active[sessionId] else { throw Err.unknownSession }
        try a.handle.close()
        active[sessionId] = nil
        let dest = uniqueURL(for: sanitize(name))
        try FileManager.default.moveItem(at: a.tmpURL, to: dest)
        return dest.path
    }

    func abort(sessionId: String)
    {
        cleanup(sessionId)
    }

    private func cleanup(_ sessionId: String)
    {
        guard let a = active[sessionId] else { return }
        try? a.handle.close()
        try? FileManager.default.removeItem(at: a.tmpURL)
        active[sessionId] = nil
    }

    // 唯一化：若已存在则插入 (1) (2)…（对应桌面 uniquePath）。
    private func uniqueURL(for name: String) -> URL
    {
        let dir = downloadDir()
        let base = (name as NSString).deletingPathExtension
        let ext = (name as NSString).pathExtension
        var candidate = dir.appendingPathComponent(name)
        var i = 1
        while FileManager.default.fileExists(atPath: candidate.path)
        {
            let next = ext.isEmpty ? "\(base) (\(i))" : "\(base) (\(i)).\(ext)"
            candidate = dir.appendingPathComponent(next)
            i += 1
        }
        return candidate
    }

    // 文件名 sanitize：只取末段、剥路径分隔符，防目录穿越（对应桌面 sanitizeFileName）。
    private func sanitize(_ name: String) -> String
    {
        let last = (name as NSString).lastPathComponent
        let cleaned = last.replacingOccurrences(of: "/", with: "_")
        return cleaned.isEmpty ? "download" : cleaned
    }

    enum Err: Error
    {
        case unknownSession
    }
}
