import Foundation

// Wire DTOs. Property names match the backend JSON (Jackson camelCase). Optionals mirror values
// the backend fills only at a later ride stage.

// MARK: auth-service
struct PhoneRequest: Encodable { let phone: String }
struct VerifyOtpRequest: Encodable { let phone: String; let code: String }
struct AuthResponse: Decodable {
    let accessToken: String
    /// Exchanged for a new access token when this one expires.
    let refreshToken: String?
    let tokenType: String
    let expiresInSeconds: Int
    let userId: String
    let status: String
}

// MARK: fleet-service
struct NearbyScooter: Decodable, Identifiable {
    let id: String
    let qrCode: String
    let batteryPercent: Int
    let lat: Double
    let lng: Double
    let distanceMeters: Double
}

// MARK: zone-service

struct ZonePoint: Decodable {
    let lat: Double
    let lng: Double
}

/// A parking spot as the rider sees it.
///
/// Closed spots are returned too, flagged rather than hidden: a rider walking towards a spot that
/// shut at 22:00 needs to know it is shut, and dropping it from the map just looks like it does
/// not exist.
struct ParkingSpot: Decodable, Identifiable {
    let id: String
    let riderNameEn: String?
    let riderNameAr: String?
    let note: String?
    let polygon: [ZonePoint]
    let centre: ZonePoint?
    let openNow: Bool
    let open24x7: Bool
    let opensAt: String?
    let closesAt: String?
    let totalCapacity: Int
    /// What the rider pays to leave a vehicle here, in the ride's currency. Zero means free.
    let parkingFee: Double
    /// Derived by the server from the fee, so the two can never disagree.
    let free: Bool
    let distanceMeters: Int
}

struct MatchedParking: Decodable {
    let id: String
    let riderNameEn: String?
    let riderNameAr: String?
    let totalCapacity: Int
    let parkingFee: Double
    let free: Bool
}

struct ZoneEvaluation: Decodable {
    let endAllowed: Bool
    let speedLimit: Int?
    let parkingRequired: Bool
    let openParking: [MatchedParking]
}

// MARK: booking-service
struct AllocateRideRequest: Encodable { let qrCode: String; let lat: Double?; let lng: Double? }
struct EndRideRequest: Encodable { let lat: Double; let lng: Double; let photoUrl: String }
struct RideResponse: Decodable {
    let id: String
    let scooterId: String
    /// The code on the vehicle. Null for rides recorded before booking captured it.
    let qrCode: String?
    let status: String  // PENDING_UNLOCK | ACTIVE | ENDING | COMPLETED | ...
    let currency: String
    let unlockFee: Double
    let perMinuteRate: Double
    let fareAmount: Double?
    let requestedAt: String
    let startedAt: String?
    let endedAt: String?
    /// Set while the ride is inside its low-balance grace period: the fare has passed the wallet
    /// and the ride will be ended automatically unless the rider tops up.
    let lowBalanceWarnedAt: String?
    /// Set while the scooter under this ride is nearly flat and the ride will be closed soon.
    let lowBatteryWarnedAt: String?
}

/// A document the rider must agree to, with the version in force and where to read it.
struct LegalDocument: Decodable, Identifiable, Equatable {
    let key: String
    let version: String
    let url: String

    var id: String { key }
}

struct AcceptConsentRequest: Encodable {
    let document: String
    let version: String
}
struct InvoiceResponse: Decodable {
    let id: String
    let rideId: String
    let amount: Double
    let currency: String
    let durationSeconds: Int
    let createdAt: String
}

// MARK: user-wallet-service
struct WalletResponse: Decodable { let userId: String; let balance: Double; let currency: String }

// MARK: payment-service
struct TopupRequest: Encodable { let userId: String; let packageCode: String?; let amount: Double? }
struct PaymentResponse: Decodable {
    let id: String
    let userId: String
    let amount: Double
    let currency: String
    let packageCode: String?
    let status: String
    let paymentUrl: String?
    let createdAt: String
}

/// Shape of the backend's RFC-7807 ProblemDetail error body.
struct ProblemDetail: Decodable { let detail: String?; let message: String? }

/// Body of POST /api/v1/auth/refresh. The token is opaque, so the user id travels with it.
struct RefreshRequest: Encodable {
    let userId: String
    let refreshToken: String
}
