import SwiftUI
import ContactsUI
import Contacts

// MARK: - ContactPickerBridge

/// Hält CNContactPickerViewController-Delegation am Leben.
/// Als @State in der View speichern, dann presentMulti/presentSingle direkt
/// aus dem Button-Action aufrufen — kein Modifier, kein isPresented-Flag.
///
/// Findet automatisch den obersten UIViewController im App-Fenster und
/// präsentiert den Picker von dort. Funktioniert aus fullScreenCover,
/// Sheet und jedem anderen Kontext. Keine Permission nötig.
final class ContactPickerBridge: NSObject, CNContactPickerDelegate {

    // internal (not private) damit Unit-Tests den Callback direkt setzen können
    var pendingCallback: (([CNContact]) -> Void)?

    // MARK: - Presentation

    func presentMulti(completion: @escaping ([CNContact]) -> Void) {
        pendingCallback = completion
        showPicker()
    }

    func presentSingle(completion: @escaping (CNContact) -> Void) {
        pendingCallback = { contacts in
            if let first = contacts.first { completion(first) }
        }
        showPicker()
    }

    private func showPicker() {
        // Auf nächsten Run-Loop-Cycle warten, damit SwiftUI seinen aktuellen
        // Update-Pass abgeschlossen hat bevor UIKit modal präsentiert wird.
        DispatchQueue.main.async { [weak self] in
            guard let self else { return }
            guard let top = self.topmostViewController() else { return }
            let picker = CNContactPickerViewController()
            picker.delegate = self
            top.present(picker, animated: true)
        }
    }

    private func topmostViewController() -> UIViewController? {
        // Aktive UIWindowScene bevorzugen, auf erste zurückfallen
        let scene = UIApplication.shared.connectedScenes
            .compactMap { $0 as? UIWindowScene }
            .first { $0.activationState == .foregroundActive }
            ?? UIApplication.shared.connectedScenes
                .compactMap { $0 as? UIWindowScene }
                .first

        // iOS 15+: scene.keyWindow ist zuverlässiger als windows.first(where: isKeyWindow)
        guard let window = scene?.keyWindow ?? scene?.windows.first,
              let root = window.rootViewController else { return nil }

        var top: UIViewController = root
        while let presented = top.presentedViewController { top = presented }
        return top
    }

    // MARK: - CNContactPickerDelegate

    func contactPicker(_ picker: CNContactPickerViewController, didSelect contacts: [CNContact]) {
        pendingCallback?(contacts)
        pendingCallback = nil
    }

    func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact) {
        pendingCallback?([contact])
        pendingCallback = nil
    }

    func contactPickerDidCancel(_ picker: CNContactPickerViewController) {
        pendingCallback = nil
    }
}

// MARK: - MultiContactPickerTrigger

/// Invisible UIViewRepresentable that presents CNContactPickerViewController
/// for multi-select. Finds the correct UIViewController by walking the UIKit
/// responder chain from the embedded UIView — more reliable than searching
/// for the globally topmost VC.
struct MultiContactPickerTrigger: UIViewRepresentable {
    @Binding var isPresented: Bool
    let onSelect: ([CNContact]) -> Void

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

    func makeUIView(context: Context) -> UIView {
        let v = UIView()
        v.isHidden = true
        context.coordinator.hostView = v
        return v
    }

    func updateUIView(_ uiView: UIView, context: Context) {
        let c = context.coordinator
        c.onSelect = onSelect
        c.dismiss = { isPresented = false }
        guard isPresented, !c.isPresenting else { return }
        c.isPresenting = true
        DispatchQueue.main.async { [weak c] in
            guard let c, let vc = c.findHostingVC() else {
                c?.isPresenting = false
                c?.dismiss()
                return
            }
            let picker = CNContactPickerViewController()
            picker.delegate = c
            vc.present(picker, animated: true)
        }
    }

    final class Coordinator: NSObject, CNContactPickerDelegate {
        var hostView: UIView!
        var onSelect: ([CNContact]) -> Void = { _ in }
        var dismiss: () -> Void = {}
        var isPresenting = false

        func findHostingVC() -> UIViewController? {
            var responder: UIResponder? = hostView
            while let next = responder?.next {
                if let vc = next as? UIViewController { return vc }
                responder = next
            }
            return nil
        }

        // Multi-select delegate (presence of this method enables multi-select UI)
        func contactPicker(_ picker: CNContactPickerViewController, didSelect contacts: [CNContact]) {
            onSelect(contacts)
            dismiss()
            isPresenting = false
        }

        func contactPickerDidCancel(_ picker: CNContactPickerViewController) {
            dismiss()
            isPresenting = false
        }
    }
}

// MARK: - SingleContactPickerTrigger

/// Like MultiContactPickerTrigger but shows a single-selection UI.
/// Only implements contactPicker(_:didSelect:) (singular) so that
/// CNContactPickerViewController switches to single-selection mode.
struct SingleContactPickerTrigger: UIViewRepresentable {
    @Binding var isPresented: Bool
    let onSelect: (CNContact) -> Void

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

    func makeUIView(context: Context) -> UIView {
        let v = UIView()
        v.isHidden = true
        context.coordinator.hostView = v
        return v
    }

    func updateUIView(_ uiView: UIView, context: Context) {
        let c = context.coordinator
        c.onSelect = onSelect
        c.dismiss = { isPresented = false }
        guard isPresented, !c.isPresenting else { return }
        c.isPresenting = true
        DispatchQueue.main.async { [weak c] in
            guard let c, let vc = c.findHostingVC() else {
                c?.isPresenting = false
                c?.dismiss()
                return
            }
            let picker = CNContactPickerViewController()
            picker.delegate = c
            vc.present(picker, animated: true)
        }
    }

    final class Coordinator: NSObject, CNContactPickerDelegate {
        var hostView: UIView!
        var onSelect: (CNContact) -> Void = { _ in }
        var dismiss: () -> Void = {}
        var isPresenting = false

        func findHostingVC() -> UIViewController? {
            var responder: UIResponder? = hostView
            while let next = responder?.next {
                if let vc = next as? UIViewController { return vc }
                responder = next
            }
            return nil
        }

        // Single-select: only this method → picker shows single-selection UI
        func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact) {
            onSelect(contact)
            dismiss()
            isPresenting = false
        }

        func contactPickerDidCancel(_ picker: CNContactPickerViewController) {
            dismiss()
            isPresenting = false
        }
    }
}

// MARK: - ContactImport (Mapping-Hilfsstruktur)

struct ContactImport {
    let name: String
    let occupation: String
    let location: String
    let birthday: Date?
    let photoData: Data?
    let phoneNumber: String?   // primäre Telefonnummer (bevorzugt Mobil/iPhone)
    let emailAddress: String?  // erste verfügbare E-Mail-Adresse
    let cnIdentifier: String?  // stabile Apple Contacts-ID für spätere Aktualisierung

    static func from(_ contact: CNContact) -> ContactImport {
        // Mittelname einbeziehen, falls vorhanden
        let parts = [contact.givenName, contact.middleName, contact.familyName].filter { !$0.isEmpty }
        let name = parts.joined(separator: " ")

        // Berufsbezeichnung und Firma kombinieren wenn beide vorhanden
        let occupation: String
        let hasJob = !contact.jobTitle.isEmpty
        let hasOrg = !contact.organizationName.isEmpty
        if hasJob && hasOrg {
            occupation = "\(contact.jobTitle) · \(contact.organizationName)"
        } else if hasJob {
            occupation = contact.jobTitle
        } else if hasOrg {
            occupation = contact.organizationName
        } else {
            occupation = ""
        }

        let location: String
        if let postal = contact.postalAddresses.first?.value {
            var parts: [String] = []
            // Straße (mehrzeilig → ", " zusammenführen)
            let street = postal.street.replacingOccurrences(of: "\n", with: ", ")
            if !street.isEmpty { parts.append(street) }
            // PLZ + Stadt zusammen, Bundesstaat separat
            let cityPart = [postal.postalCode, postal.city].filter { !$0.isEmpty }.joined(separator: " ")
            if !cityPart.isEmpty { parts.append(cityPart) }
            if !postal.state.isEmpty { parts.append(postal.state) }
            if !postal.country.isEmpty { parts.append(postal.country) }
            location = parts.joined(separator: ", ")
        } else {
            location = ""
        }

        var birthdayDate: Date? = nil
        if let components = contact.birthday {
            var resolved = components
            if resolved.year == nil || resolved.year == 1 {
                resolved.year = Calendar.current.component(.year, from: Date())
            }
            birthdayDate = Calendar.current.date(from: resolved)
        }

        let photoData: Data?
        if let thumbnail = contact.thumbnailImageData {
            photoData = thumbnail
        } else if let fullData = contact.imageData,
                  let resized = UIImage(data: fullData)?.resizedForAvatar() {
            photoData = resized.jpegData(compressionQuality: 0.8)
        } else {
            photoData = nil
        }

        // Telefon: Mobil/iPhone bevorzugen, dann erste verfügbare Nummer
        let mobileLabels = ["iPhone", "_$!<Mobile>!$_", "_$!<Main>!$_"]
        let phoneNumber: String?
        if let labeled = contact.phoneNumbers.first(where: { mobileLabels.contains($0.label ?? "") }) {
            phoneNumber = labeled.value.stringValue
        } else {
            phoneNumber = contact.phoneNumbers.first?.value.stringValue
        }

        // E-Mail: erste verfügbare Adresse
        let emailAddress = contact.emailAddresses.first.map { $0.value as String }

        return ContactImport(name: name, occupation: occupation, location: location,
                             birthday: birthdayDate, photoData: photoData,
                             phoneNumber: phoneNumber, emailAddress: emailAddress,
                             cnIdentifier: contact.identifier)
    }
}

// MARK: - UIImage helper

extension UIImage {
    func resizedForAvatar(maxSide: CGFloat = 400) -> UIImage? {
        let scale = min(maxSide / size.width, maxSide / size.height)
        guard scale < 1 else { return self }
        let newSize = CGSize(width: (size.width * scale).rounded(),
                            height: (size.height * scale).rounded())
        return UIGraphicsImageRenderer(size: newSize).image { _ in
            draw(in: CGRect(origin: .zero, size: newSize))
        }
    }
}
