import SwiftUI
import UIKit

struct MessageBubbleView: View {
    let message: Message
    let isMine: Bool
    var isGroup: Bool = false
    var isHighlighted: Bool = false
    var isCurrentSearchResult: Bool = false
    var chatClient: ChatClient?
    var currentUserId: String = ""
    var onReply: (() -> Void)?
    var onReact: ((String) -> Void)?
    var onForward: (() -> Void)?
    var onPin: ((Bool) -> Void)?
    var onDelete: (() -> Void)?

    @State private var fullImageData: Data?
    @State private var showFullImage = false
    @State private var isLoadingImage = false
    @State private var isLoadingFile = false
    @State private var downloadedFileURL: URL?
    @State private var showShareSheet = false
    @State private var imageError: String?

    var body: some View {
        HStack {
            if isMine { Spacer(minLength: 60) }

            VStack(alignment: isMine ? .trailing : .leading, spacing: 4) {
                if !isMine && isGroup {
                    Text(message.senderUsername)
                        .font(.caption.bold())
                        .foregroundStyle(.secondary)
                }

                if message.isDeleted {
                    Text("Message deleted")
                        .font(.body.italic())
                        .foregroundStyle(.secondary)
                        .padding(12)
                        .background(Color(.systemGray6))
                        .clipShape(RoundedRectangle(cornerRadius: 16))
                } else {
                    // Forwarded header
                    if let fwd = message.forwardedFrom {
                        HStack(spacing: 4) {
                            Rectangle().fill(.cyan).frame(width: 3)
                            VStack(alignment: .leading, spacing: 1) {
                                Text("Forwarded from").font(.caption2).foregroundStyle(.secondary)
                                Text(fwd.sender).font(.caption.bold()).foregroundStyle(.cyan)
                            }
                        }
                        .padding(.horizontal, 8).padding(.top, 4)
                    }

                    // Reply reference
                    if message.replyTo != nil {
                        HStack(spacing: 4) {
                            Rectangle()
                                .fill(.blue.opacity(0.5))
                                .frame(width: 2)
                            Text("Reply to message")
                                .font(.caption)
                                .foregroundStyle(.secondary)
                        }
                        .padding(.horizontal, 8)
                    }

                    // Image thumbnail
                    if let imageInfo = message.image {
                        imageView(imageInfo: imageInfo)
                    }

                    // File card
                    if let file = message.file {
                        VStack(alignment: .leading, spacing: 4) {
                            HStack {
                                if isLoadingFile {
                                    ProgressView()
                                        .scaleEffect(0.8)
                                } else {
                                    Image(systemName: fileIcon(for: file.filename))
                                }
                                Text(file.filename)
                                    .lineLimit(1)
                            }
                            .font(.subheadline)

                            Text(formatFileSize(file.size))
                                .font(.caption)
                                .foregroundStyle(.secondary)
                        }
                        .padding(12)
                        .background(Color(.systemGray5))
                        .clipShape(RoundedRectangle(cornerRadius: 12))
                        .onTapGesture {
                            downloadAndShareFile(file: file)
                        }
                    }

                    // Text content with link detection
                    if let text = message.text, !text.isEmpty {
                        LinkText(text: text, isMine: isMine)
                            .padding(12)
                            .background(
                                isMine ? Color.blue : Color(.systemGray5)
                            )
                            .clipShape(RoundedRectangle(cornerRadius: 16))
                    }

                    // Timestamp + checkmarks + reactions — all on one line
                    HStack(spacing: 4) {
                        if message.pinnedAt != nil {
                            Image(systemName: "pin.fill").font(.caption2).foregroundStyle(.orange)
                        }
                        Text(formatTime(message.createdAt)).font(.caption2).foregroundStyle(.secondary)
                        if isMine {
                            deliveryIndicator
                        }
                        if !message.reactions.isEmpty {
                            inlineReactionBadges
                        }
                    }
                    .frame(maxWidth: .infinity, alignment: isMine ? .trailing : .leading)
                }
            }
            .padding(2)
            .background(
                RoundedRectangle(cornerRadius: 16)
                    .fill(isCurrentSearchResult ? Color.orange.opacity(0.3) :
                          isHighlighted ? Color.yellow.opacity(0.2) : Color.clear)
            )
            .contextMenu {
                if !message.isDeleted {
                    Button(action: { onReply?() }) {
                        Label("Reply", systemImage: "arrowshape.turn.up.left")
                    }

                    Menu {
                        ForEach(ReactionEmoji.allowed, id: \.self) { key in
                            Button("\(ReactionEmoji.display[key] ?? "") \(key)") { onReact?(key) }
                        }
                    } label: {
                        Label("React", systemImage: "face.smiling")
                    }

                    Button(action: {
                        UIPasteboard.general.string = message.text ?? ""
                        // Auto-clear clipboard after 30 seconds
                        DispatchQueue.main.asyncAfter(deadline: .now() + 30) {
                            if UIPasteboard.general.string == message.text {
                                UIPasteboard.general.string = ""
                            }
                        }
                    }) {
                        Label("Copy", systemImage: "doc.on.doc")
                    }

                    Button(action: { onForward?() }) {
                        Label("Forward", systemImage: "arrowshape.turn.up.right")
                    }

                    Button(action: { onPin?(message.pinnedAt == nil) }) {
                        Label(message.pinnedAt == nil ? "Pin" : "Unpin",
                              systemImage: message.pinnedAt == nil ? "pin" : "pin.slash")
                    }

                    if isMine {
                        Button(role: .destructive, action: { onDelete?() }) {
                            Label("Delete", systemImage: "trash")
                        }
                    }
                }
            }

            if !isMine { Spacer(minLength: 60) }
        }
        .sheet(isPresented: $showFullImage) {
            if let data = fullImageData {
                ImageViewerView(imageData: data)
            }
        }
        .sheet(isPresented: $showShareSheet, onDismiss: {
            // Clean up decrypted temp file after sharing
            if let fileURL = downloadedFileURL {
                try? FileManager.default.removeItem(at: fileURL)
                downloadedFileURL = nil
            }
        }) {
            if let fileURL = downloadedFileURL {
                ActivityViewController(activityItems: [fileURL])
            }
        }
    }

    // MARK: - Reaction Badges (inline — used in timestamp row)

    private var inlineReactionBadges: some View {
        let grouped = Dictionary(grouping: message.reactions, by: \.reaction)
        return HStack(spacing: 2) {
            ForEach(grouped.sorted(by: { $0.key < $1.key }), id: \.key) { reaction, users in
                Button {
                    onReact?(reaction)
                } label: {
                    Text(ReactionEmoji.display[reaction] ?? reaction)
                        .font(.caption)
                }
                .buttonStyle(.plain)
            }
        }
    }

    // MARK: - Delivery Indicator (checkmarks)

    @ViewBuilder
    private var deliveryIndicator: some View {
        let isRead = message.readBy.contains(where: { $0 != "__delivered__" && $0 != currentUserId })
        let isDelivered = message.readBy.contains("__delivered__")

        if isRead {
            // Read: 2 green checkmarks
            HStack(spacing: -4) {
                Image(systemName: "checkmark").font(.system(size: 9, weight: .bold))
                Image(systemName: "checkmark").font(.system(size: 9, weight: .bold))
            }
            .foregroundStyle(.green)
        } else if isDelivered {
            // Delivered: 2 gray checkmarks
            HStack(spacing: -4) {
                Image(systemName: "checkmark").font(.system(size: 9, weight: .bold))
                Image(systemName: "checkmark").font(.system(size: 9, weight: .bold))
            }
            .foregroundStyle(.secondary)
        } else {
            // Sent: 1 gray checkmark
            Image(systemName: "checkmark").font(.system(size: 9, weight: .bold))
                .foregroundStyle(.secondary)
        }
    }

    // MARK: - Image View

    @ViewBuilder
    private func imageView(imageInfo: ImageInfo) -> some View {
        VStack(spacing: 4) {
            if let thumbB64 = imageInfo.thumbnail,
               let thumbData = Data(base64Encoded: thumbB64),
               let uiImage = UIImage(data: thumbData) {
                Image(uiImage: uiImage)
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .frame(maxWidth: 220, maxHeight: 220)
                    .clipShape(RoundedRectangle(cornerRadius: 12))
                    .overlay {
                        if isLoadingImage {
                            RoundedRectangle(cornerRadius: 12)
                                .fill(.black.opacity(0.4))
                            ProgressView()
                                .tint(.white)
                        }
                    }
                    .onTapGesture {
                        downloadAndShowFullImage(imageInfo: imageInfo)
                    }
            } else {
                // No thumbnail available — show placeholder
                RoundedRectangle(cornerRadius: 12)
                    .fill(Color(.systemGray5))
                    .frame(width: 160, height: 120)
                    .overlay {
                        if isLoadingImage {
                            ProgressView()
                        } else {
                            VStack(spacing: 6) {
                                Image(systemName: "photo")
                                    .font(.title2)
                                Text(imageInfo.filename)
                                    .font(.caption)
                                    .lineLimit(1)
                            }
                            .foregroundStyle(.secondary)
                        }
                    }
                    .onTapGesture {
                        downloadAndShowFullImage(imageInfo: imageInfo)
                    }
            }

            if let error = imageError {
                Text(error)
                    .font(.caption2)
                    .foregroundStyle(.red)
                    .onTapGesture {
                        imageError = nil
                        downloadAndShowFullImage(imageInfo: imageInfo)
                    }
            }
        }
    }

    private func downloadAndShowFullImage(imageInfo: ImageInfo) {
        guard !isLoadingImage, let client = chatClient else { return }
        // If already downloaded, show immediately
        if fullImageData != nil {
            showFullImage = true
            return
        }
        imageError = nil
        isLoadingImage = true
        Task {
            guard let aesKey = try? ProtocolHandler.decodeBinary(imageInfo.aesKey),
                  let iv = try? ProtocolHandler.decodeBinary(imageInfo.iv) else {
                await MainActor.run {
                    isLoadingImage = false
                    imageError = "Failed to decode image keys"
                }
                return
            }
            let data = await client.downloadFile(fileId: imageInfo.fileId, aesKey: aesKey, iv: iv, conversationId: message.conversationId)
            await MainActor.run {
                isLoadingImage = false
                if let data = data {
                    fullImageData = data
                    showFullImage = true
                } else {
                    imageError = "Download failed, tap to retry"
                }
            }
        }
    }

    // MARK: - File Download

    private func downloadAndShareFile(file: FileInfo) {
        guard !isLoadingFile, let client = chatClient else { return }
        // If already downloaded, show share sheet immediately
        if downloadedFileURL != nil {
            showShareSheet = true
            return
        }
        isLoadingFile = true
        Task {
            guard let aesKey = try? ProtocolHandler.decodeBinary(file.aesKey),
                  let iv = try? ProtocolHandler.decodeBinary(file.iv) else {
                await MainActor.run { isLoadingFile = false }
                return
            }
            let data = await client.downloadFile(fileId: file.fileId, aesKey: aesKey, iv: iv, conversationId: message.conversationId)
            await MainActor.run {
                isLoadingFile = false
                if let data = data {
                    // Save to temp with file protection, clean up on dismiss
                    let tempDir = FileManager.default.temporaryDirectory
                    let fileURL = tempDir.appendingPathComponent(file.filename)
                    try? data.write(to: fileURL, options: .completeFileProtection)
                    downloadedFileURL = fileURL
                    showShareSheet = true
                }
            }
        }
    }

    private func fileIcon(for filename: String) -> String {
        let ext = (filename as NSString).pathExtension.lowercased()
        switch ext {
        case "pdf": return "doc.richtext"
        case "doc", "docx": return "doc.text"
        case "xls", "xlsx": return "tablecells"
        case "ppt", "pptx": return "rectangle.on.rectangle"
        case "zip", "rar", "7z": return "doc.zipper"
        case "mp3", "wav", "m4a": return "music.note"
        case "mp4", "mov", "avi": return "film"
        case "txt": return "doc.plaintext"
        default: return "paperclip"
        }
    }

    // MARK: - Helpers

    private func formatTime(_ date: Date) -> String {
        let formatter = DateFormatter()
        if Calendar.current.isDateInToday(date) {
            formatter.dateFormat = "HH:mm"
        } else {
            formatter.dateFormat = "MMM d, HH:mm"
        }
        return formatter.string(from: date)
    }

    private func formatFileSize(_ bytes: Int) -> String {
        if bytes < 1024 { return "\(bytes) B" }
        if bytes < 1024 * 1024 { return "\(bytes / 1024) KB" }
        return String(format: "%.1f MB", Double(bytes) / (1024 * 1024))
    }
}

// MARK: - Link Text

struct LinkText: View {
    let text: String
    let isMine: Bool

    var body: some View {
        Text(buildAttributedString())
            .environment(\.openURL, OpenURLAction { url in
                UIApplication.shared.open(url)
                return .handled
            })
    }

    private func buildAttributedString() -> AttributedString {
        var result = AttributedString()

        guard let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue) else {
            return appendPlainWithMentions(text[text.startIndex..<text.endIndex], to: &result)
        }

        let nsRange = NSRange(text.startIndex..., in: text)
        let matches = detector.matches(in: text, range: nsRange)

        if matches.isEmpty {
            return appendPlainWithMentions(text[text.startIndex..<text.endIndex], to: &result)
        }

        var lastEnd = text.startIndex

        for match in matches {
            guard let matchRange = Range(match.range, in: text),
                  let url = match.url else { continue }

            // Plain text before link (with mention highlighting)
            if lastEnd < matchRange.lowerBound {
                appendPlainWithMentions(text[lastEnd..<matchRange.lowerBound], to: &result)
            }

            // Link
            let isSecure = url.scheme?.lowercased() == "https"
            var link = AttributedString(text[matchRange])
            link.link = url
            link.underlineStyle = .single
            if isMine {
                link.foregroundColor = isSecure ? .cyan : .red
            } else {
                link.foregroundColor = isSecure ? .blue : .red
            }
            result.append(link)

            lastEnd = matchRange.upperBound
        }

        // Remaining text
        if lastEnd < text.endIndex {
            appendPlainWithMentions(text[lastEnd..<text.endIndex], to: &result)
        }

        return result
    }

    private static let mentionRegex = try! NSRegularExpression(pattern: "@(\\w+)", options: [])

    @discardableResult
    private func appendPlainWithMentions(_ substring: Substring, to result: inout AttributedString) -> AttributedString {
        let str = String(substring)
        let nsRange = NSRange(str.startIndex..., in: str)
        let matches = Self.mentionRegex.matches(in: str, range: nsRange)

        if matches.isEmpty {
            var plain = AttributedString(str)
            plain.foregroundColor = isMine ? .white : .primary
            result.append(plain)
            return result
        }

        let mentionColor = Color(red: 0.537, green: 0.706, blue: 0.980)
        var lastEnd = str.startIndex

        for match in matches {
            guard let matchRange = Range(match.range, in: str) else { continue }

            if lastEnd < matchRange.lowerBound {
                var plain = AttributedString(str[lastEnd..<matchRange.lowerBound])
                plain.foregroundColor = isMine ? .white : .primary
                result.append(plain)
            }

            var mention = AttributedString(str[matchRange])
            mention.foregroundColor = mentionColor
            mention.font = .body.bold()
            result.append(mention)

            lastEnd = matchRange.upperBound
        }

        if lastEnd < str.endIndex {
            var plain = AttributedString(str[lastEnd..<str.endIndex])
            plain.foregroundColor = isMine ? .white : .primary
            result.append(plain)
        }

        return result
    }
}

// MARK: - Flow Layout

struct FlowLayout: Layout {
    var spacing: CGFloat = 4

    func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
        let maxWidth = proposal.width ?? .infinity
        var x: CGFloat = 0
        var y: CGFloat = 0
        var rowHeight: CGFloat = 0

        for subview in subviews {
            let size = subview.sizeThatFits(.unspecified)
            if x + size.width > maxWidth && x > 0 {
                x = 0
                y += rowHeight + spacing
                rowHeight = 0
            }
            x += size.width + spacing
            rowHeight = max(rowHeight, size.height)
        }
        return CGSize(width: maxWidth, height: y + rowHeight)
    }

    func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
        var x = bounds.minX
        var y = bounds.minY
        var rowHeight: CGFloat = 0

        for subview in subviews {
            let size = subview.sizeThatFits(.unspecified)
            if x + size.width > bounds.maxX && x > bounds.minX {
                x = bounds.minX
                y += rowHeight + spacing
                rowHeight = 0
            }
            subview.place(at: CGPoint(x: x, y: y), proposal: .unspecified)
            x += size.width + spacing
            rowHeight = max(rowHeight, size.height)
        }
    }
}

// MARK: - Share Sheet

struct ActivityViewController: UIViewControllerRepresentable {
    let activityItems: [Any]

    func makeUIViewController(context: Context) -> UIActivityViewController {
        UIActivityViewController(activityItems: activityItems, applicationActivities: nil)
    }

    func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {}
}
