import SwiftUI

// MARK: - MessageFileView

/// Displays a file attachment inside a message bubble.
///
/// Desktop parity: `MessageFile.tsx` — shows file icon + filename + size card.
/// Tap to download (if not cached), then share via UIActivityViewController.
///
/// Preview format: "tag::filesize::filename" (desktop parity).
struct MessageFileView: View {

    let attachment: MessageAttachment
    let message: ChatMessage
    let outgoing: Bool

    @State private var isDownloading = false
    @State private var downloadProgress: String = ""
    @State private var isDownloaded = false
    @State private var downloadError = false
    @State private var cachedFileURL: URL?

    var body: some View {
        HStack(spacing: 10) {
            // File icon circle (Telegram parity: solid accent fill + white icon)
            ZStack {
                Circle()
                    .fill(downloadError
                        ? Color.red.opacity(0.15)
                        : (outgoing ? Color.white.opacity(0.2) : Color(hex: 0x008BFF)))
                    .frame(width: 44, height: 44)

                if isDownloading {
                    ProgressView()
                        .tint(.white)
                        .scaleEffect(0.8)
                } else if downloadError {
                    Image(systemName: "exclamationmark.circle.fill")
                        .font(.system(size: 20))
                        .foregroundStyle(.red)
                } else if isDownloaded {
                    Image(systemName: fileIcon)
                        .font(.system(size: 20))
                        .foregroundStyle(.white)
                } else {
                    Image(systemName: "arrow.down")
                        .font(.system(size: 20, weight: .medium))
                        .foregroundStyle(.white)
                }
            }

            // File metadata
            VStack(alignment: .leading, spacing: 2) {
                // Telegram parity: filename in accent blue (incoming) or white (outgoing)
                Text(fileName)
                    .font(.system(size: 16, weight: .regular))
                    .foregroundStyle(outgoing ? .white : Color(hex: 0x008BFF))
                    .lineLimit(1)

                if isDownloading {
                    Text("Downloading...")
                        .font(.system(size: 13).monospacedDigit())
                        .foregroundStyle(outgoing ? .white.opacity(0.5) : RosettaColors.Adaptive.textSecondary)
                } else if downloadError {
                    Text("File expired")
                        .font(.system(size: 13))
                        .foregroundStyle(RosettaColors.error)
                } else {
                    Text(formattedFileSize)
                        .font(.system(size: 13).monospacedDigit())
                        .foregroundStyle(
                            outgoing ? .white.opacity(0.5) : RosettaColors.Adaptive.textSecondary
                        )
                }
            }

            Spacer(minLength: 0)
        }
        .padding(.horizontal, 10)
        .padding(.vertical, 8)
        .contentShape(Rectangle())
        .task {
            checkCache()
        }
        // Download/share triggered by BubbleContextMenuOverlay tap → notification.
        // Overlay UIView intercepts all taps; SwiftUI onTapGesture can't fire.
        .onReceive(NotificationCenter.default.publisher(for: .triggerAttachmentDownload)) { notif in
            if let id = notif.object as? String, id == attachment.id {
                if isDownloaded, let url = cachedFileURL {
                    shareFile(url)
                } else if !isDownloading {
                    downloadFile()
                }
            }
        }
    }

    // MARK: - Metadata Parsing

    /// Parses "tag::filesize::filename" preview format.
    private var fileMetadata: (tag: String, size: Int, name: String) {
        let parsed = AttachmentPreviewCodec.parseFilePreview(attachment.preview)
        return (parsed.downloadTag, parsed.fileSize, parsed.fileName)
    }

    private var fileName: String { fileMetadata.name }
    private var fileSize: Int { fileMetadata.size }
    private var fileTag: String {
        let effective = attachment.effectiveDownloadTag
        return effective.isEmpty ? fileMetadata.tag : effective
    }

    private var formattedFileSize: String {
        let bytes = fileSize
        if bytes < 1024 { return "\(bytes) B" }
        if bytes < 1024 * 1024 { return String(format: "%.1f KB", Double(bytes) / 1024) }
        return String(format: "%.1f MB", Double(bytes) / (1024 * 1024))
    }

    private var fileIcon: String {
        let ext = (fileName as NSString).pathExtension.lowercased()
        switch ext {
        case "pdf": return "doc.fill"
        case "zip", "rar", "7z": return "doc.zipper"
        case "jpg", "jpeg", "png", "gif": return "photo.fill"
        case "mp4", "mov", "avi": return "film.fill"
        case "mp3", "wav", "aac": return "waveform"
        default: return "doc.fill"
        }
    }

    // MARK: - Download

    private func checkCache() {
        if let url = AttachmentCache.shared.fileURL(forAttachmentId: attachment.id, fileName: fileName) {
            cachedFileURL = url
            isDownloaded = true
        }
    }

    private func downloadFile() {
        guard !isDownloading, !fileTag.isEmpty else { return }
        guard let storedPassword = message.attachmentPassword, !storedPassword.isEmpty else {
            downloadError = true
            return
        }

        isDownloading = true
        downloadError = false

        Task {
            do {
                let encryptedData = try await TransportManager.shared.downloadFile(tag: fileTag, server: attachment.transportServer)
                let encryptedString = String(decoding: encryptedData, as: UTF8.self)

                let passwords = MessageCrypto.attachmentPasswordCandidates(from: storedPassword)
                let decryptedData = decryptFileData(
                    encryptedString: encryptedString, passwords: passwords
                )
                guard let decryptedData else { throw TransportError.invalidResponse }

                // Parse data URI if present, otherwise use raw data
                let fileData: Data
                if let decryptedString = String(data: decryptedData, encoding: .utf8),
                   decryptedString.hasPrefix("data:"),
                   let commaIndex = decryptedString.firstIndex(of: ",") {
                    let base64Part = String(decryptedString[decryptedString.index(after: commaIndex)...])
                    fileData = Data(base64Encoded: base64Part) ?? decryptedData
                } else {
                    fileData = decryptedData
                }

                let url = AttachmentCache.shared.saveFile(
                    fileData, forAttachmentId: attachment.id, fileName: fileName
                )

                await MainActor.run {
                    cachedFileURL = url
                    isDownloaded = true
                    isDownloading = false
                }
            } catch {
                await MainActor.run {
                    downloadError = true
                    isDownloading = false
                }
            }
        }
    }

    /// Tries each password candidate with requireCompression to avoid wrong-key garbage.
    private func decryptFileData(encryptedString: String, passwords: [String]) -> Data? {
        let crypto = CryptoManager.shared
        for password in passwords {
            if let data = try? crypto.decryptWithPassword(
                encryptedString, password: password, requireCompression: true
            ) {
                return data
            }
        }
        // Fallback: try without requireCompression (legacy uncompressed payloads)
        for password in passwords {
            if let data = try? crypto.decryptWithPassword(
                encryptedString, password: password
            ) {
                return data
            }
        }
        return nil
    }

    // MARK: - Share

    private func shareFile(_ cachedURL: URL) {
        // Files are stored encrypted (.enc) on disk. Decrypt to temp dir for sharing.
        guard let data = AttachmentCache.shared.loadFileData(
            forAttachmentId: attachment.id, fileName: fileName
        ) else { return }
        let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent(fileName)
        try? data.write(to: tempURL, options: .atomic)

        let activityVC = UIActivityViewController(
            activityItems: [tempURL],
            applicationActivities: nil
        )
        if let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
           let window = scene.windows.first,
           let rootVC = window.rootViewController {
            var topVC = rootVC
            while let presented = topVC.presentedViewController {
                topVC = presented
            }
            if let popover = activityVC.popoverPresentationController {
                popover.sourceView = topVC.view
                popover.sourceRect = CGRect(x: topVC.view.bounds.midX, y: topVC.view.bounds.midY, width: 0, height: 0)
            }
            topVC.present(activityVC, animated: true)
        }
    }
}
