import Foundation
import AppKit

public actor UploadManager {
    private let client: APIClient
    private let config: SyncConfig
    
    public init(client: APIClient, config: SyncConfig) {
        self.client = client
        self.config = config
    }
    
    public func uploadFile(
        _ url: URL,
        duplicateAction: UploadDuplicateAction? = nil,
        onProgress: UploadProgressHandler? = nil
    ) async throws -> UploadResult {
        let response = try await client.uploadFile(
            url: url,
            folder: config.defaultUploadFolder,
            duplicateAction: duplicateAction,
            onProgress: onProgress
        )
        let fullURL = resolvedURL(response.url ?? response.attachmentUrl)
        
        // Copy URL to clipboard
        await MainActor.run {
            NSPasteboard.general.clearContents()
            NSPasteboard.general.setString(fullURL.absoluteString, forType: .string)
        }
        
        return UploadResult(
            filename: url.lastPathComponent,
            hash: response.hash,
            url: fullURL,
            copiedToClipboard: true
        )
    }
    
    public func uploadData(
        _ data: Data,
        filename: String,
        duplicateAction: UploadDuplicateAction? = nil,
        onProgress: UploadProgressHandler? = nil
    ) async throws -> UploadResult {
        let response = try await client.uploadData(
            data,
            filename: filename,
            folder: config.defaultUploadFolder,
            duplicateAction: duplicateAction,
            onProgress: onProgress
        )
        let fullURL = resolvedURL(response.url ?? response.attachmentUrl)
        
        // Copy URL to clipboard
        await MainActor.run {
            NSPasteboard.general.clearContents()
            NSPasteboard.general.setString(fullURL.absoluteString, forType: .string)
        }
        
        return UploadResult(
            filename: filename,
            hash: response.hash,
            url: fullURL,
            copiedToClipboard: true
        )
    }
    
    public func createNote(from text: String, title: String? = nil) async throws -> DocumentSaveResponse {
        let noteTitle = title ?? generateTitle(from: text)
        let folder = config.defaultUploadFolder ?? ""
        let path = folder.isEmpty ? "\(sanitizeFilename(noteTitle)).md" : "\(folder)/\(sanitizeFilename(noteTitle)).md"
        
        let content = "# \(noteTitle)\n\n\(text)"
        
        return try await client.saveDocument(path: path, content: content)
    }
    
    private func generateTitle(from text: String) -> String {
        let lines = text.split(separator: "\n", omittingEmptySubsequences: true)
        if let firstLine = lines.first {
            let title = String(firstLine).trimmingCharacters(in: .whitespaces)
            if title.count > 60 {
                return String(title.prefix(60)) + "..."
            }
            return title
        }
        return "Note \(ISO8601DateFormatter().string(from: Date()))"
    }
    
    private func sanitizeFilename(_ name: String) -> String {
        let invalidChars = CharacterSet(charactersIn: ":/\\?%*|\"<>").union(.controlCharacters)
        return name.components(separatedBy: invalidChars).joined(separator: "-")
    }

    private func resolvedURL(_ path: String) -> URL {
        URL(string: path, relativeTo: client.baseURL)?.absoluteURL ?? client.baseURL.appendingPathComponent(path)
    }
}

public struct UploadResult {
    public let filename: String
    public let hash: String
    public let url: URL
    public let copiedToClipboard: Bool
}

public struct UploadCancelledError: LocalizedError {
    public init() {}

    public var errorDescription: String? { "Upload cancelled" }
}

@MainActor
public func promptForUploadDuplicate(_ error: APIError, filename: String) -> UploadDuplicateAction? {
    let alert = NSAlert()
    alert.alertStyle = .warning

    if error.conflictType == "document" {
        alert.messageText = "A document named \(filename) already exists"
        alert.informativeText = "Choose whether to replace the inbox document or keep both files."
        alert.addButton(withTitle: "Keep Both")
        alert.addButton(withTitle: "Replace")
        alert.addButton(withTitle: "Cancel")

        switch alert.runModal() {
        case .alertFirstButtonReturn:
            return .rename
        case .alertSecondButtonReturn:
            return .overwrite
        default:
            return nil
        }
    }

    alert.messageText = "\(filename) was already uploaded"
    alert.informativeText = "The server already has the same file content. Use its existing URL?"
    alert.addButton(withTitle: "Copy Existing URL")
    alert.addButton(withTitle: "Cancel")
    return alert.runModal() == .alertFirstButtonReturn ? .reuse : nil
}
