import AVKit
import MapKit
import QuickLook
import SwiftUI

struct ConversationMessageContainer: View {
    let message: Message
    let isOutgoing: Bool

    var body: some View {
        if let msgText = message.body, msgText.isLocation {
            EmbededMapView(location: msgText.getLatLon)
        } else if let msgText = message.body, msgText.isContact {
            ContactView(message: message)
        } else if message.attachmentType != nil {
            AttachmentView(message: message)
        } else {
            Text(message.body ?? "...")
                .font(.body2)
                .foregroundColor(.Material.Text.main)
                .multilineTextAlignment(.leading)
                .padding(10)
        }
    }
}

struct MessageAttr: View {
    let message: Message

    var body: some View {
        VStack(alignment: .leading, spacing: 0) {
            Text(message.date, style: .time)
                .font(.sub2)
                .foregroundColor(.Material.Shape.separator)
            Spacer()
            if message.sentError {
                Image(systemName: "exclamationmark.circle")
                    .font(.body3)
                    .foregroundColor(.Rainbow.red500)
            } else if message.pending {
                Image(systemName: "clock")
                    .font(.body3)
                    .foregroundColor(.Material.Shape.separator)
            }
        }
    }
}

private struct EmbededMapView: View {
    let location: CLLocationCoordinate2D

    var body: some View {
        Map(
            coordinateRegion: .constant(MKCoordinateRegion(center: location, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))),
            interactionModes: [],
            showsUserLocation: false,
            userTrackingMode: .none,
            annotationItems: [location],
            annotationContent: { _ in
                MapMarker(coordinate: location, tint: .blue)
            }
        )
        .frame(width: Const.mapPreviewSize, height: Const.mapPreviewSize)
        .onTapGesture {
            let mapItem = MKMapItem(placemark: MKPlacemark(coordinate: location))
            mapItem.name = "Location"
            mapItem.openInMaps(launchOptions: [MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving])
        }
    }
}

private struct ContactView: View {
    let message: Message

    var body: some View {
        VStack {
            ZStack {
                Circle()
                    .frame(width: 44, height: 44)
                    .foregroundColor(contactName.firstLetterColor)
                Text(contactName.firstLetter)
                    .foregroundColor(.white)
                    .font(.body1)
            }
            Text(message.body?.getContactJid ?? "...")
                .font(.body2)
                .foregroundColor(.Material.Text.main)
                .multilineTextAlignment(.leading)
        }
        .padding()
        .onTapGesture {
            // TODO: Jump to add roster from here
        }
    }

    private var contactName: String {
        message.body?.getContactJid ?? "?"
    }
}

private struct AttachmentView: View {
    let message: Message

    var body: some View {
        if message.attachmentDownloadFailed || (message.attachmentLocalName != nil && message.sentError) {
            failed
        } else {
            switch message.attachmentType {
            case .image:
                if let thumbnail = thumbnail() {
                    thumbnail
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                        .frame(width: Const.attachmentPreviewSize, height: Const.attachmentPreviewSize)
                } else {
                    placeholder
                }

            case .movie:
                if let file = message.attachmentLocalPath {
                    VideoPlayerView(url: file)
                        .frame(width: Const.attachmentPreviewSize, height: Const.attachmentPreviewSize)
                } else {
                    placeholder
                }

            case .file:
                if let file = message.attachmentLocalPath {
                    DocumentPreview(url: file)
                        .frame(width: Const.attachmentPreviewSize, height: Const.attachmentPreviewSize)
                } else {
                    placeholder
                }

            default:
                placeholder
            }
        }
    }

    @ViewBuilder private var placeholder: some View {
        Rectangle()
            .foregroundColor(.Material.Background.dark)
            .frame(width: Const.attachmentPreviewSize, height: Const.attachmentPreviewSize)
            .overlay {
                ZStack {
                    ProgressView()
                        .scaleEffect(1.5)
                        .progressViewStyle(CircularProgressViewStyle(tint: .Material.Elements.active))
                    let imageName = progressImageName(message.attachmentType ?? .file)
                    Image(systemName: imageName)
                        .font(.body1)
                        .foregroundColor(.Material.Elements.active)
                }
            }
    }

    @ViewBuilder private var failed: some View {
        Rectangle()
            .foregroundColor(.Material.Background.dark)
            .frame(width: Const.attachmentPreviewSize, height: Const.attachmentPreviewSize)
            .overlay {
                ZStack {
                    VStack {
                        Text(L10n.Attachment.Downloading.retry)
                            .font(.body3)
                            .foregroundColor(.Rainbow.red500)
                        Image(systemName: "exclamationmark.arrow.triangle.2.circlepath")
                            .font(.body1)
                            .foregroundColor(.Rainbow.red500)
                    }
                }
            }
            .onTapGesture {
                if let url = message.attachmentRemotePath {
                    store.dispatch(.fileAction(.downloadAttachmentFile(messageId: message.id, attachmentRemotePath: url)))
                } else if message.attachmentLocalName != nil && message.sentError {
                    store.dispatch(.sharingAction(.retrySharing(messageId: message.id)))
                }
            }
    }

    private func progressImageName(_ type: MessageAttachmentType) -> String {
        switch type {
        case .image:
            return "photo"
        case .audio:
            return "music.note"
        case .movie:
            return "film"
        case .file:
            return "doc"
        }
    }

    private func thumbnail() -> Image? {
        guard let thumbnailPath = message.attachmentThumbnailPath else { return nil }
        guard let uiImage = UIImage(contentsOfFile: thumbnailPath.path()) else { return nil }
        return Image(uiImage: uiImage)
    }
}

// TODO: Make video player better!
private struct VideoPlayerView: UIViewControllerRepresentable {
    let url: URL

    func makeUIViewController(context _: Context) -> AVPlayerViewController {
        let controller = AVPlayerViewController()
        controller.player = AVPlayer(url: url)
        controller.allowsPictureInPicturePlayback = true
        return controller
    }

    func updateUIViewController(_: AVPlayerViewController, context _: Context) {
        // Update the controller if needed.
    }
}

struct DocumentPreview: UIViewControllerRepresentable {
    var url: URL

    func makeUIViewController(context: Context) -> QLPreviewController {
        let controller = QLPreviewController()
        controller.dataSource = context.coordinator
        return controller
    }

    func updateUIViewController(_: QLPreviewController, context _: Context) {
        // Update the controller if needed.
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    class Coordinator: NSObject, QLPreviewControllerDataSource {
        var parent: DocumentPreview

        init(_ parent: DocumentPreview) {
            self.parent = parent
        }

        func numberOfPreviewItems(in _: QLPreviewController) -> Int {
            1
        }

        func previewController(_: QLPreviewController, previewItemAt _: Int) -> QLPreviewItem {
            parent.url as QLPreviewItem
        }
    }
}