import CoreLocation
import MapKit
import SwiftUI
import UIKit

struct TimelineLocationButton: View {
    @Binding var location: TimelineLocation?
    @State private var showLocationPicker = false

    var body: some View {
        Button {
            showLocationPicker = true
        } label: {
            HStack(spacing: 10) {
                Image(systemName: location == nil ? "location" : "location.fill")
                    .font(.system(size: 13, weight: .medium))
                    .foregroundStyle(location == nil ? Color.secondary : Color.lessCosmosGold)
                    .frame(width: 24, height: 24)

                VStack(alignment: .leading, spacing: 2) {
                    Text(location?.name ?? "所在位置")
                        .font(.system(size: 13, weight: .medium))
                        .foregroundStyle(Color.primary)
                        .lineLimit(1)

                    Text(locationSubtitle)
                        .font(.system(size: 10))
                        .foregroundStyle(Color.secondary)
                        .lineLimit(1)
                }

                Spacer(minLength: 8)

                Image(systemName: "chevron.right")
                    .font(.system(size: 9, weight: .medium))
                    .foregroundStyle(Color.secondary.opacity(0.6))
            }
            .padding(.horizontal, 12)
            .padding(.vertical, 10)
            .background(Color.cardBackground.opacity(0.35))
            .businessBorder(cornerRadius: 8)
            .contentShape(Rectangle())
        }
        .buttonStyle(.plain)
        .accessibilityLabel(location == nil ? "添加所在位置" : "修改所在位置")
        .accessibilityValue(location?.name ?? "尚未添加")
        .sheet(isPresented: $showLocationPicker) {
            TimelineLocationPickerSheet(selection: $location)
        }
    }

    private var locationSubtitle: String {
        guard let location else { return "点击后根据 GPS 选择当前位置" }
        return location.subtitle.isEmpty ? "已添加位置" : location.subtitle
    }
}

struct TimelineLocationPickerSheet: View {
    @Environment(\.dismiss) private var dismiss
    @Binding var selection: TimelineLocation?

    @StateObject private var locationService = TimelineLocationService()
    @State private var selectedLocation: TimelineLocation?
    @State private var nearbyLocations: [TimelineLocation] = []
    @State private var searchResults: [TimelineLocation] = []
    @State private var cameraPosition: MapCameraPosition = .automatic
    @State private var searchText = ""
    @State private var isLoadingPlaces = false
    @State private var placeError: String?
    @State private var hasMadeSelection = false

    init(selection: Binding<TimelineLocation?>) {
        _selection = selection
        _selectedLocation = State(initialValue: selection.wrappedValue)
    }

    var body: some View {
        NavigationStack {
            VStack(spacing: 0) {
                locationMap
                    .frame(height: 220)

                Divider()
                    .overlay(Color.lineBorder)

                locationList
            }
            .background(Color.spaceBlack)
            .navigationTitle("所在位置")
            .navigationBarTitleDisplayMode(.inline)
            .searchable(
                text: $searchText,
                placement: .navigationBarDrawer(displayMode: .always),
                prompt: "搜索附近地点"
            )
            .onSubmit(of: .search) {
                Task {
                    await searchPlaces()
                }
            }
            .onChange(of: searchText) { _, newValue in
                if newValue.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
                    searchResults = []
                    placeError = nil
                }
            }
            .toolbar {
                ToolbarItem(placement: .cancellationAction) {
                    Button("取消") {
                        dismiss()
                    }
                }

                ToolbarItem(placement: .confirmationAction) {
                    Button("完成") {
                        selection = selectedLocation
                        dismiss()
                    }
                }
            }
        }
        .onAppear {
            locationService.requestCurrentLocation()
            if let selectedLocation {
                centerMap(on: selectedLocation)
            }
        }
        .onChange(of: locationService.currentLocation) { _, location in
            guard let location else { return }
            if selectedLocation == nil, !hasMadeSelection {
                selectedLocation = location
            }
            centerMap(on: selectedLocation ?? location)
            Task {
                await loadNearbyPlaces(around: location)
            }
        }
        .presentationDetents([.large])
        .presentationDragIndicator(.visible)
        .presentationBackground(Color.spaceBlack)
    }

    private var locationMap: some View {
        Map(position: $cameraPosition) {
            UserAnnotation()

            if let selectedLocation {
                Marker(
                    selectedLocation.name,
                    coordinate: coordinate(for: selectedLocation)
                )
                .tint(Color.lessCosmosGold)
            }
        }
        .mapStyle(.standard(pointsOfInterest: .all))
        .overlay(alignment: .bottomTrailing) {
            Button {
                locationService.requestCurrentLocation()
            } label: {
                Image(systemName: "location.fill")
                    .font(.system(size: 14, weight: .semibold))
                    .foregroundStyle(Color.primary)
                    .frame(width: 38, height: 38)
                    .background(.ultraThinMaterial, in: Circle())
            }
            .padding(12)
            .accessibilityLabel("重新定位")
        }
    }

    private var locationList: some View {
        ScrollView {
            LazyVStack(spacing: 0) {
                noLocationRow

                if let currentLocation = locationService.currentLocation {
                    locationRow(
                        currentLocation,
                        icon: "location.fill",
                        iconColor: Color.lessCosmosGold
                    )
                } else {
                    locatingRow
                }

                if isLoadingPlaces {
                    HStack(spacing: 10) {
                        ProgressView()
                        Text(searchText.isEmpty ? "正在查找附近地点…" : "正在搜索地点…")
                            .font(.system(size: 12))
                            .foregroundStyle(Color.secondary)
                        Spacer()
                    }
                    .padding(.horizontal, 20)
                    .padding(.vertical, 16)
                }

                ForEach(displayedLocations, id: \.self) { location in
                    locationRow(location)
                }

                if let message = locationService.errorMessage ?? placeError {
                    locationErrorRow(message)
                }
            }
        }
        .scrollDismissesKeyboard(.interactively)
    }

    private var noLocationRow: some View {
        Button {
            hasMadeSelection = true
            selectedLocation = nil
        } label: {
            HStack(spacing: 12) {
                Image(systemName: "location.slash")
                    .font(.system(size: 13, weight: .medium))
                    .foregroundStyle(Color.secondary)
                    .frame(width: 28)

                Text("不显示位置")
                    .font(.system(size: 14, weight: .medium))
                    .foregroundStyle(Color.primary)

                Spacer()

                if selectedLocation == nil {
                    Image(systemName: "checkmark")
                        .font(.system(size: 13, weight: .semibold))
                        .foregroundStyle(Color.lessCosmosGold)
                }
            }
            .padding(.horizontal, 20)
            .padding(.vertical, 15)
            .contentShape(Rectangle())
        }
        .buttonStyle(.plain)
        .overlay(alignment: .bottom) {
            Divider()
                .padding(.leading, 60)
                .overlay(Color.lineBorder)
        }
    }

    private var locatingRow: some View {
        HStack(spacing: 12) {
            if locationService.isLocating {
                ProgressView()
                    .frame(width: 28)
            } else {
                Image(systemName: "location")
                    .foregroundStyle(Color.secondary)
                    .frame(width: 28)
            }

            VStack(alignment: .leading, spacing: 3) {
                Text(locationService.isLocating ? "正在定位…" : "当前位置")
                    .font(.system(size: 14, weight: .medium))
                    .foregroundStyle(Color.primary)

                Text("用于查找你附近的地点")
                    .font(.system(size: 11))
                    .foregroundStyle(Color.secondary)
            }

            Spacer()
        }
        .padding(.horizontal, 20)
        .padding(.vertical, 14)
    }

    private func locationRow(
        _ location: TimelineLocation,
        icon: String = "mappin.and.ellipse",
        iconColor: Color = Color.secondary
    ) -> some View {
        Button {
            hasMadeSelection = true
            selectedLocation = location
            centerMap(on: location)
        } label: {
            HStack(spacing: 12) {
                Image(systemName: icon)
                    .font(.system(size: 13, weight: .medium))
                    .foregroundStyle(iconColor)
                    .frame(width: 28)

                VStack(alignment: .leading, spacing: 3) {
                    Text(location.name)
                        .font(.system(size: 14, weight: .medium))
                        .foregroundStyle(Color.primary)
                        .lineLimit(1)

                    if !location.subtitle.isEmpty {
                        Text(location.subtitle)
                            .font(.system(size: 11))
                            .foregroundStyle(Color.secondary)
                            .lineLimit(2)
                    }
                }

                Spacer(minLength: 8)

                if selectedLocation == location {
                    Image(systemName: "checkmark")
                        .font(.system(size: 13, weight: .semibold))
                        .foregroundStyle(Color.lessCosmosGold)
                }
            }
            .padding(.horizontal, 20)
            .padding(.vertical, 13)
            .contentShape(Rectangle())
        }
        .buttonStyle(.plain)
        .overlay(alignment: .bottom) {
            Divider()
                .padding(.leading, 60)
                .overlay(Color.lineBorder.opacity(0.7))
        }
    }

    private func locationErrorRow(_ message: String) -> some View {
        VStack(alignment: .leading, spacing: 10) {
            Text(message)
                .font(.system(size: 12))
                .foregroundStyle(Color.secondary)

            if locationService.isAuthorizationDenied {
                Button("前往系统设置") {
                    guard let settingsURL = URL(string: UIApplication.openSettingsURLString) else {
                        return
                    }
                    UIApplication.shared.open(settingsURL)
                }
                .font(.system(size: 12, weight: .semibold))
            } else {
                Button("重新定位") {
                    locationService.requestCurrentLocation()
                }
                .font(.system(size: 12, weight: .semibold))
            }
        }
        .frame(maxWidth: .infinity, alignment: .leading)
        .padding(20)
    }

    private var displayedLocations: [TimelineLocation] {
        searchText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
            ? nearbyLocations
            : searchResults
    }

    @MainActor
    private func loadNearbyPlaces(around location: TimelineLocation) async {
        isLoadingPlaces = true
        placeError = nil
        defer { isLoadingPlaces = false }

        let request = MKLocalPointsOfInterestRequest(
            center: coordinate(for: location),
            radius: 2_000
        )
        request.pointOfInterestFilter = .includingAll

        do {
            let response = try await MKLocalSearch(request: request).start()
            nearbyLocations = uniqueLocations(
                response.mapItems.compactMap(location(from:))
            )
        } catch {
            placeError = "附近地点暂时无法加载，你仍可使用 GPS 当前位置。"
        }
    }

    @MainActor
    private func searchPlaces() async {
        let query = searchText.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !query.isEmpty,
              let center = locationService.currentLocation ?? selectedLocation else {
            return
        }

        isLoadingPlaces = true
        placeError = nil
        defer { isLoadingPlaces = false }

        let request = MKLocalSearch.Request()
        request.naturalLanguageQuery = query
        request.region = MKCoordinateRegion(
            center: coordinate(for: center),
            latitudinalMeters: 10_000,
            longitudinalMeters: 10_000
        )

        do {
            let response = try await MKLocalSearch(request: request).start()
            searchResults = uniqueLocations(
                response.mapItems.compactMap(location(from:))
            )
            if searchResults.isEmpty {
                placeError = "没有找到相关地点，请换一个关键词。"
            }
        } catch {
            placeError = "地点搜索失败，请稍后重试。"
        }
    }

    private func location(from mapItem: MKMapItem) -> TimelineLocation? {
        guard let name = mapItem.name?
            .trimmingCharacters(in: .whitespacesAndNewlines),
              !name.isEmpty else {
            return nil
        }

        let coordinate = mapItem.placemark.coordinate
        return TimelineLocation(
            name: name,
            address: mapItem.placemark.title ?? "",
            latitude: coordinate.latitude,
            longitude: coordinate.longitude
        )
    }

    private func uniqueLocations(
        _ locations: [TimelineLocation]
    ) -> [TimelineLocation] {
        var keys = Set<String>()
        return locations.filter { location in
            let key = [
                location.name,
                String(format: "%.5f", location.latitude),
                String(format: "%.5f", location.longitude)
            ].joined(separator: "|")
            return keys.insert(key).inserted
        }
    }

    private func centerMap(on location: TimelineLocation) {
        cameraPosition = .region(
            MKCoordinateRegion(
                center: coordinate(for: location),
                latitudinalMeters: 1_200,
                longitudinalMeters: 1_200
            )
        )
    }

    private func coordinate(
        for location: TimelineLocation
    ) -> CLLocationCoordinate2D {
        CLLocationCoordinate2D(
            latitude: location.latitude,
            longitude: location.longitude
        )
    }
}

private final class TimelineLocationService: NSObject, ObservableObject {
    @Published private(set) var currentLocation: TimelineLocation?
    @Published private(set) var isLocating = false
    @Published private(set) var errorMessage: String?
    @Published private(set) var authorizationStatus: CLAuthorizationStatus

    private let manager = CLLocationManager()
    private let geocoder = CLGeocoder()

    override init() {
        authorizationStatus = manager.authorizationStatus
        super.init()
        manager.delegate = self
        manager.desiredAccuracy = kCLLocationAccuracyBest
    }

    var isAuthorizationDenied: Bool {
        authorizationStatus == .denied || authorizationStatus == .restricted
    }

    func requestCurrentLocation() {
        errorMessage = nil

        switch manager.authorizationStatus {
        case .notDetermined:
            isLocating = true
            manager.requestWhenInUseAuthorization()
        case .authorizedAlways, .authorizedWhenInUse:
            isLocating = true
            manager.requestLocation()
        case .denied, .restricted:
            isLocating = false
            errorMessage = "定位权限未开启。你可以前往系统设置允许访问当前位置。"
        @unknown default:
            isLocating = false
            errorMessage = "暂时无法确认定位权限。"
        }
    }
}

extension TimelineLocationService: CLLocationManagerDelegate {
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        authorizationStatus = manager.authorizationStatus
        if manager.authorizationStatus == .authorizedAlways
            || manager.authorizationStatus == .authorizedWhenInUse {
            isLocating = true
            manager.requestLocation()
        } else if manager.authorizationStatus == .denied
            || manager.authorizationStatus == .restricted {
            isLocating = false
            errorMessage = "定位权限未开启。你可以前往系统设置允许访问当前位置。"
        }
    }

    func locationManager(
        _ manager: CLLocationManager,
        didUpdateLocations locations: [CLLocation]
    ) {
        guard let location = locations.last else { return }
        isLocating = false

        geocoder.reverseGeocodeLocation(location) { [weak self] placemarks, error in
            DispatchQueue.main.async {
                guard let self else { return }

                if let placemark = placemarks?.first {
                    self.currentLocation = TimelineLocation(
                        name: "当前位置",
                        address: Self.address(from: placemark),
                        latitude: location.coordinate.latitude,
                        longitude: location.coordinate.longitude
                    )
                } else {
                    self.currentLocation = TimelineLocation(
                        name: "当前位置",
                        address: String(
                            format: "%.6f, %.6f",
                            location.coordinate.latitude,
                            location.coordinate.longitude
                        ),
                        latitude: location.coordinate.latitude,
                        longitude: location.coordinate.longitude
                    )
                    if error != nil {
                        self.errorMessage = "已取得 GPS 位置，但暂时无法解析详细地址。"
                    }
                }
            }
        }
    }

    func locationManager(
        _ manager: CLLocationManager,
        didFailWithError error: Error
    ) {
        isLocating = false
        if (error as? CLError)?.code == .locationUnknown {
            errorMessage = "暂时无法确定当前位置，请移到开阔区域后重试。"
        } else {
            errorMessage = "定位失败，请检查系统定位服务后重试。"
        }
    }

    private static func address(from placemark: CLPlacemark) -> String {
        var parts: [String] = []
        for part in [
            placemark.administrativeArea,
            placemark.locality,
            placemark.subLocality,
            placemark.thoroughfare,
            placemark.subThoroughfare,
            placemark.name
        ] {
            guard let part = part?.trimmingCharacters(in: .whitespacesAndNewlines),
                  !part.isEmpty,
                  !parts.contains(part) else {
                continue
            }
            parts.append(part)
        }
        return parts.joined(separator: " ")
    }
}
