import UIKit
import Social
import UniformTypeIdentifiers

/// Mail (and any other text/URL source) Share Extension. Parses
/// flight info out of the shared content using the same regex
/// patterns as the calendar importer, writes the result to an App
/// Group UserDefaults entry under `pendingMailShare`, and dismisses.
///
/// The main app reads that entry on next foreground (via
/// PendingShareWatcher) and pops the AddFlightView prefilled with
/// whatever we parsed.
final class ShareViewController: SLComposeServiceViewController {

    private var parsed: ParsedFlight?
    private var allText: String = ""

    struct ParsedFlight {
        let flightDate: Date
        let carrierIATA: String?
        let flightNumber: String?
        let departureIATA: String?
        let arrivalIATA: String?
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        title = "Add to Flights"
        placeholder = "Optional note"
        loadSharedItems()
    }

    private func loadSharedItems() {
        guard let extensionItems = extensionContext?.inputItems as? [NSExtensionItem] else { return }
        let group = DispatchGroup()
        var accumulated = ""

        for item in extensionItems {
            // Mail surfaces both the subject line (as the contentText)
            // and the body (as attachments). We absorb both.
            if let content = item.attributedContentText?.string, !content.isEmpty {
                accumulated += " " + content
            }
            for provider in item.attachments ?? [] {
                if provider.hasItemConformingToTypeIdentifier(UTType.plainText.identifier) {
                    group.enter()
                    provider.loadItem(forTypeIdentifier: UTType.plainText.identifier, options: nil) { item, _ in
                        defer { group.leave() }
                        if let s = item as? String { accumulated += " " + s }
                    }
                }
                if provider.hasItemConformingToTypeIdentifier(UTType.url.identifier) {
                    group.enter()
                    provider.loadItem(forTypeIdentifier: UTType.url.identifier, options: nil) { item, _ in
                        defer { group.leave() }
                        if let u = item as? URL { accumulated += " " + u.absoluteString }
                    }
                }
            }
        }

        group.notify(queue: .main) { [weak self] in
            guard let self else { return }
            self.allText = accumulated
            self.parsed = Self.parseFlight(from: accumulated)
            self.validateContent()
        }
    }

    override func isContentValid() -> Bool {
        return parsed != nil
    }

    override func didSelectPost() {
        guard let parsed else {
            extensionContext?.completeRequest(returningItems: [], completionHandler: nil)
            return
        }
        // Hand the parsed flight off to the host app via a custom URL
        // scheme. Share Extensions can't call UIApplication.shared
        // directly, but we can walk the responder chain to find one
        // that implements `openURL:` and invoke it. iOS still routes
        // it through the host app correctly.
        var comps = URLComponents()
        comps.scheme = "flights"
        comps.host = "import"
        var items: [URLQueryItem] = [
            URLQueryItem(name: "date", value: String(parsed.flightDate.timeIntervalSince1970))
        ]
        if let c = parsed.carrierIATA   { items.append(.init(name: "carrier", value: c)) }
        if let f = parsed.flightNumber  { items.append(.init(name: "num",     value: f)) }
        if let d = parsed.departureIATA { items.append(.init(name: "dep",     value: d)) }
        if let a = parsed.arrivalIATA   { items.append(.init(name: "arr",     value: a)) }
        comps.queryItems = items
        if let url = comps.url {
            openURLInHost(url)
        }
        extensionContext?.completeRequest(returningItems: [], completionHandler: nil)
    }

    /// Walk the responder chain looking for an object that implements
    /// `openURL:`. UIApplication is one. Invoking it from a share
    /// extension launches the host app via its registered URL scheme.
    private func openURLInHost(_ url: URL) {
        var responder: UIResponder? = self
        let selector = NSSelectorFromString("openURL:")
        while responder != nil {
            if responder!.responds(to: selector) {
                _ = responder!.perform(selector, with: url)
                return
            }
            responder = responder?.next
        }
    }

    override func configurationItems() -> [Any]! {
        return []
    }

    // MARK: - Parser

    private static func parseFlight(from text: String) -> ParsedFlight? {
        guard let flightMatch = matchFlight(in: text) else { return nil }
        let route = matchRoute(in: text)
        let date = matchDate(in: text) ?? Date()
        return ParsedFlight(
            flightDate: date,
            carrierIATA: flightMatch.carrier,
            flightNumber: flightMatch.number,
            departureIATA: route?.from,
            arrivalIATA: route?.to
        )
    }

    private static func matchFlight(in s: String) -> (carrier: String, number: String)? {
        let pattern = "([A-Z]{2,3})\\s*([0-9]{1,4})"
        guard let regex = try? NSRegularExpression(pattern: pattern) else { return nil }
        let nsRange = NSRange(s.startIndex..., in: s)
        let denylist: Set<String> = ["AM", "PM", "ET", "PT", "CT", "MT", "US", "UK", "TO", "AS"]
        for m in regex.matches(in: s, range: nsRange) where m.numberOfRanges == 3 {
            guard let cRange = Range(m.range(at: 1), in: s),
                  let nRange = Range(m.range(at: 2), in: s) else { continue }
            let carrier = String(s[cRange])
            if denylist.contains(carrier) { continue }
            return (carrier, String(s[nRange]))
        }
        return nil
    }

    private static func matchRoute(in s: String) -> (from: String, to: String)? {
        let pattern = "([A-Z]{3})\\s*(?:[-→>]|to)\\s*([A-Z]{3})"
        guard let regex = try? NSRegularExpression(pattern: pattern) else { return nil }
        let nsRange = NSRange(s.startIndex..., in: s)
        guard let m = regex.firstMatch(in: s, range: nsRange), m.numberOfRanges == 3,
              let fRange = Range(m.range(at: 1), in: s),
              let tRange = Range(m.range(at: 2), in: s) else { return nil }
        return (String(s[fRange]), String(s[tRange]))
    }

    private static func matchDate(in s: String) -> Date? {
        // ISO-ish: "May 27, 2026" / "27 May 2026" / "2026-05-27"
        let formatters: [String] = [
            "MMMM d, yyyy",
            "MMM d, yyyy",
            "d MMMM yyyy",
            "d MMM yyyy",
            "yyyy-MM-dd",
            "MM/dd/yyyy"
        ]
        // Try matching against any substring with each formatter.
        for fmt in formatters {
            let df = DateFormatter()
            df.dateFormat = fmt
            df.locale = Locale(identifier: "en_US_POSIX")
            // Slide a window through the text; for date formats with
            // word months we need substrings starting with a month.
            let words = s.split(whereSeparator: { !$0.isLetter && !$0.isNumber && $0 != "-" && $0 != "/" && $0 != "," }).map(String.init)
            for i in 0..<words.count {
                for end in min(i + 4, words.count)...(min(i + 4, words.count)) {
                    let candidate = words[i..<end].joined(separator: " ")
                    if let date = df.date(from: candidate) {
                        return date
                    }
                }
            }
        }
        return nil
    }
}
