import SwiftUI
import PhotosUI
import SwiftData
import CoreLocation
import MapKit

// ---------------------------------------------------------------------------
// MARK: - EditCarView
// ---------------------------------------------------------------------------
/// A comprehensive, production‑ready view for editing an existing `Car`
/// record.  Designed for Xcode 16 / iOS 18 and SwiftData.
///
/// **Features**
/// - Latitude / Longitude fields with on‑the‑fly validation
/// - Address text field + one‑tap reverse‑geocode option
/// - Car details: make, model, year, color
/// - License plate entry
/// - Car image picker via `PhotosPicker`
/// - Auto‑saves on disappear (or explicit toolbar button)
struct EditCarView: View {

    // -----------------------------------------------------------------------
    // MARK: Data
    // -----------------------------------------------------------------------

    /// The car being edited – a SwiftData `@Bindable` so every change is
    /// automatically tracked by the model context.
    @Bindable var car: Car

    /// The SwiftData context needed to save changes.
    @Environment(\.modelContext) private var modelContext

    /// Standard dismiss action.
    @Environment(\.dismiss) private var dismiss

    // -----------------------------------------------------------------------
    // MARK: Image picker state
    // -----------------------------------------------------------------------

    /// Holds the PhotosPicker selection; we convert it to `Data` manually.
    @State private var selectedPhotoItem: PhotosPickerItem?

    /// When true the photo picker sheet is presented.
    @State private var showPhotoPicker = false

    // -----------------------------------------------------------------------
    // MARK: Geocoding state
    // -----------------------------------------------------------------------

    private let geocoder = CLGeocoder()

    /// Controls the geocoding spinner.
    @State private var isGeocoding = false

    /// Holds a one‑line status message (success / error).
    @State private var geocodeStatus: String?

    // -----------------------------------------------------------------------
    // MARK: Validation state
    // -----------------------------------------------------------------------

    /// Set to `true` after the user attempts to save, so inline error
    /// messages appear.
    @State private var didAttemptSave = false

    // -----------------------------------------------------------------------
    // MARK: Errors / alerts
    // -----------------------------------------------------------------------

    @State private var alertTitle = ""
    @State private var alertMessage = ""
    @State private var showAlert = false

    // =======================================================================
    // MARK: Body
    // =======================================================================

    var body: some View {
        NavigationStack {
            formContent
                .navigationTitle("Edit Car")
                .navigationBarTitleDisplayMode(.inline)
                .toolbar { toolbarContent }
                .alert(alertTitle, isPresented: $showAlert) {
                    Button("OK") { showAlert = false }
                } message: {
                    Text(alertMessage)
                }
                .onChange(of: selectedPhotoItem) { _, newItem in
                    handleSelectedPhotoItem(newItem)
                }
                .interactiveDismissDisabled(hasUnsavedChanges)
        }
    }

    // =======================================================================
    // MARK: - Form content
    // =======================================================================

    /// The scrollable form, broken into logical sections.
    @MainActor
    @ViewBuilder
    private var formContent: some View {
        Form {
            photoSection
            locationSection
            detailsSection
            licenseSection
        }
    }

    // -----------------------------------------------------------------------
    // MARK: Photo section
    // -----------------------------------------------------------------------

    /// Allows the user to pick or replace the car image via PhotosPicker.
    @MainActor
    private var photoSection: some View {
        Section {
            // Tappable image area
            Button {
                showPhotoPicker = true
            } label: {
                ZStack {
                    if let data = car.imageData, let uiImage = UIImage(data: data) {
                        Image(uiImage: uiImage)
                            .resizable()
                            .scaledToFill()
                            .frame(height: 200)
                            .clipped()
                            .clipShape(RoundedRectangle(cornerRadius: 12))
                    } else {
                        RoundedRectangle(cornerRadius: 12)
                            .fill(Color.secondary.opacity(0.15))
                            .frame(height: 200)
                            .overlay {
                                VStack(spacing: 8) {
                                    Image(systemName: "photo.badge.plus")
                                        .font(.largeTitle)
                                        .foregroundStyle(.secondary)
                                    Text("Tap to add photo")
                                        .font(.subheadline)
                                        .foregroundStyle(.secondary)
                                }
                            }
                    }

                    // Visual "replace" badge in the top‑trailing corner
                    if car.imageData != nil {
                        VStack {
                            HStack {
                                Spacer()
                                Image(systemName: "pencil.circle.fill")
                                    .font(.title2)
                                    .foregroundStyle(.white, Color.accentColor)
                                    .shadow(radius: 2)
                                    .padding(8)
                            }
                            Spacer()
                        }
                    }
                }
            }
            .buttonStyle(.plain)
            .photosPicker(
                isPresented: $showPhotoPicker,
                selection: $selectedPhotoItem,
                matching: .images,
                photoLibrary: .shared()
            )

            if car.imageData != nil {
                Button("Remove Photo", role: .destructive) {
                    withAnimation {
                        car.imageData = nil
                    }
                }
                .frame(maxWidth: .infinity, alignment: .center)
            }
        } header: {
            Label("Car Photo", systemImage: "camera")
        }
    }

    // -----------------------------------------------------------------------
    // MARK: Location section
    // -----------------------------------------------------------------------

    /// Latitude, longitude, and address fields with live validation and a
    /// geocode button that reverse‑lookups the address from coordinates.
    @MainActor
    private var locationSection: some View {
        Section {
            // ---------- Latitude ----------
            validatedTextField(
                label: "Latitude",
                value: Binding<Double>(
                    get: { car.latitude },
                    set: { car.latitude = $0 }
                ),
                format: FloatingPointFormatStyle<Double>.number.precision(.fractionLength(6)),
                validation: validateLatitude,
                errorMessage: "Latitude must be between -90 and 90."
            )

            // ---------- Longitude ----------
            validatedTextField(
                label: "Longitude",
                value: Binding<Double>(
                    get: { car.longitude },
                    set: { car.longitude = $0 }
                ),
                format: FloatingPointFormatStyle<Double>.number.precision(.fractionLength(6)),
                validation: validateLongitude,
                errorMessage: "Longitude must be between -180 and 180."
            )

            // ---------- Address ----------
            HStack(alignment: .top) {
                VStack(alignment: .leading, spacing: 4) {
                    TextField("Address", text: $car.address, axis: .vertical)
                        .textFieldStyle(.plain)
                        .lineLimit(2 ... 4)
                        .autocorrectionDisabled()

                    if didAttemptSave && car.address.trimmingCharacters(in: .whitespaces).isEmpty {
                        Text("Address is required.")
                            .font(.caption)
                            .foregroundStyle(.red)
                    }
                }

                // Geocode button — only enabled when coordinates are valid
                Button {
                    reverseGeocode()
                } label: {
                    if isGeocoding {
                        ProgressView()
                            .controlSize(.small)
                    } else {
                        Image(systemName: "arrow.triangle.swap")
                    }
                }
                .buttonStyle(.borderless)
                .disabled(!car.hasValidCoordinate || isGeocoding)
                .help("Look up address from coordinates")
            }

            // Geocoding status message
            if let status = geocodeStatus {
                Text(status)
                    .font(.caption)
                    .foregroundStyle(.secondary)
            }
        } header: {
            Label("Location", systemImage: "mappin.and.ellipse")
        } footer: {
            Text("Enter decimal degrees (e.g. 37.7749). Tap the swap button to reverse‑geocode.")
        }
    }

    // -----------------------------------------------------------------------
    // MARK: Car details section
    // -----------------------------------------------------------------------

    /// Make, model, year, and color fields.
    @MainActor
    private var detailsSection: some View {
        Section {
            TextField("Make (e.g. Tesla)", text: $car.make)
                .autocorrectionDisabled()
                .textInputAutocapitalization(.words)

            TextField("Model (e.g. Model 3)", text: $car.model)
                .autocorrectionDisabled()
                .textInputAutocapitalization(.words)

            HStack {
                TextField("Year", value: $car.year, format: .number)
                    .keyboardType(.numberPad)
            }

            TextField("Color", text: $car.color)
                .autocorrectionDisabled()
                .textInputAutocapitalization(.words)
        } header: {
            Label("Car Details", systemImage: "car")
        }
    }

    // -----------------------------------------------------------------------
    // MARK: License plate section
    // -----------------------------------------------------------------------

    @MainActor
    private var licenseSection: some View {
        Section {
            TextField("License Plate", text: $car.licensePlate)
                .autocorrectionDisabled()
                .textInputAutocapitalization(.allCharacters)
                .monospacedDigit()

            // Optional: validation hint
            if didAttemptSave && car.licensePlate.trimmingCharacters(in: .whitespaces).isEmpty {
                Text("License plate is required.")
                    .font(.caption)
                    .foregroundStyle(.red)
            }
        } header: {
            Label("License Plate", systemImage: "number")
        } footer: {
            Text("Enter the plate exactly as it appears on the vehicle.")
        }
    }

    // =======================================================================
    // MARK: - Toolbar
    // =======================================================================

    /// Save button (leading) and Cancel (trailing) — matches platform HIG.
    @ToolbarContentBuilder
    private var toolbarContent: some ToolbarContent {
        ToolbarItem(placement: .cancellationAction) {
            Button("Cancel") {
                dismiss()
            }
        }

        ToolbarItem(placement: .confirmationAction) {
            Button("Save") {
                saveCar()
            }
            .disabled(!formIsValid)
        }
    }

    // =======================================================================
    // MARK: - Validation
    // =======================================================================

    /// Returns `true` when the minimum required fields are non‑empty.
    private var formIsValid: Bool {
        !car.licensePlate.trimmingCharacters(in: .whitespaces).isEmpty
            && !car.address.trimmingCharacters(in: .whitespaces).isEmpty
            && validateLatitude(car.latitude)
            && validateLongitude(car.longitude)
    }

    /// Returns `true` when there are edits that have not yet been saved.
    private var hasUnsavedChanges: Bool {
        // A simplistic check: we treat any non‑empty plate as "dirty".
        // In production you would snapshot the original and diff.
        !car.licensePlate.trimmingCharacters(in: .whitespaces).isEmpty
    }

    // -----------------------------------------------------------------------
    // MARK: Coordinate validators
    // -----------------------------------------------------------------------

    private func validateLatitude(_ value: Double) -> Bool {
        (-90 ... 90).contains(value)
    }

    private func validateLongitude(_ value: Double) -> Bool {
        (-180 ... 180).contains(value)
    }

    // -----------------------------------------------------------------------
    // MARK: Reusable validated TextField (Double)
    // -----------------------------------------------------------------------

    /// Builds a `TextField` bound to a `Double` with inline validation.
    @ViewBuilder
    private func validatedTextField(
        label: String,
        value: Binding<Double>,
        format: FloatingPointFormatStyle<Double>,
        validation: (Double) -> Bool,
        errorMessage: String
    ) -> some View {
        VStack(alignment: .leading, spacing: 4) {
            TextField(label, value: value, format: format)
                .keyboardType(.decimalPad)

            if didAttemptSave && !validation(value.wrappedValue) {
                Text(errorMessage)
                    .font(.caption)
                    .foregroundStyle(.red)
            }
        }
    }

    // =======================================================================
    // MARK: - Actions
    // =======================================================================

    // -----------------------------------------------------------------------
    // MARK: Save
    // -----------------------------------------------------------------------

    /// Validates the entire form and persists to SwiftData.
    private func saveCar() {
        didAttemptSave = true

        guard formIsValid else {
            alertTitle   = "Incomplete Form"
            alertMessage = "Please fill in all required fields and correct any errors before saving."
            showAlert    = true
            return
        }

        // Update the timestamp so the UI reflects "last edited".
        car.updatedAt = Date()

        // SwiftData automatically tracks @Bindable changes, but an explicit
        // save guarantees the data is written immediately.
        do {
            try modelContext.save()
            dismiss()
        } catch {
            alertTitle   = "Save Failed"
            alertMessage = error.localizedDescription
            showAlert    = true
        }
    }

    // -----------------------------------------------------------------------
    // MARK: Reverse geocode
    // -----------------------------------------------------------------------

    /// Converts the current `latitude` / `longitude` to a human‑readable
    /// address via `CLGeocoder` and populates the address field.
    private func reverseGeocode() {
        guard car.hasValidCoordinate else { return }

        isGeocoding   = true
        geocodeStatus = "Looking up address…"

        let location = CLLocation(latitude: car.latitude, longitude: car.longitude)

        geocoder.reverseGeocodeLocation(location) { [self] placemarks, error in
            // Switch back to the main actor for UI updates.
            Task { @MainActor in
                isGeocoding = false

                if let error {
                    geocodeStatus = "Geocoding failed: \(error.localizedDescription)"
                    return
                }

                guard let mark = placemarks?.first else {
                    geocodeStatus = "No address found for these coordinates."
                    return
                }

                // Format a concise, single‑line address.
                let parts = [
                    mark.subThoroughfare,       // street number
                    mark.thoroughfare,           // street name
                    mark.locality,               // city
                    mark.administrativeArea,     // state
                    mark.postalCode              // ZIP
                ]
                .compactMap { $0 }
                .filter { !$0.isEmpty }

                car.address    = parts.joined(separator: ", ")
                geocodeStatus  = nil
            }
        }
    }

    // -----------------------------------------------------------------------
    // MARK: Photo picker handler
    // -----------------------------------------------------------------------

    /// Loads the selected `PhotosPickerItem` into `car.imageData` as JPEG
    /// data, compressing to 0.8 quality for a reasonable file size.
    private func handleSelectedPhotoItem(_ item: PhotosPickerItem?) {
        guard let item else { return }

        Task {
            guard let data = try? await item.loadTransferable(type: Data.self) else {
                await showTemporaryAlert(title: "Photo Error",
                                         message: "Could not load the selected image.")
                return
            }

            // Downsize / compress to JPEG to keep the SwiftData store lean.
            if let image = UIImage(data: data),
               let jpegData = image.jpegData(compressionQuality: 0.8) {
                await MainActor.run {
                    car.imageData = jpegData
                }
            } else {
                // Fall back to the raw data if compression fails.
                await MainActor.run {
                    car.imageData = data
                }
            }
        }
    }

    // -----------------------------------------------------------------------
    // MARK: Transient alert helper
    // -----------------------------------------------------------------------

    private func showTemporaryAlert(title: String, message: String) async {
        await MainActor.run {
            alertTitle   = title
            alertMessage = message
            showAlert    = true
        }
    }
}

// ===========================================================================
// MARK: - Preview
// ===========================================================================

#Preview("Edit Car") {
    // A preview that creates a sample container and car.
    let config = ModelConfiguration(isStoredInMemoryOnly: true)
    let container = try! ModelContainer(for: Car.self, configurations: config)

    let sample = Car(
        licensePlate: "7ABC123",
        nickname: "Blue Lightning",
        latitude: 37.7749,
        longitude: -122.4194,
        address: "San Francisco, CA",
        make: "Tesla",
        model: "Model 3",
        year: 2024,
        color: "Deep Blue Metallic"
    )

    EditCarView(car: sample)
        .modelContainer(container)
}
