//
//  PunchPointModels.swift
//  suixinkan
//

import CoreLocation
import Foundation

/// 打卡点列表筛选类型，对齐 Android `PunchPointFilterType`。
enum PunchPointFilterType: Int, CaseIterable, Sendable, Equatable, Hashable {
    case all = 0
    case inOperation = 1
    case paused = 2
    case pendingReview = 3
    case rejected = 4

    /// 页面展示文案。
    var title: String {
        switch self {
        case .all: "全部"
        case .inOperation: "运营中"
        case .paused: "已暂停"
        case .pendingReview: "未审核"
        case .rejected: "审核未通过"
        }
    }

    /// 服务端 `status` 参数。
    var apiStatus: Int { rawValue }
}

/// 打卡点列表响应。
struct PunchPointListResponse: Decodable, Sendable, Equatable {
    let total: Int
    let list: [PunchPointItem]

    init(total: Int = 0, list: [PunchPointItem] = []) {
        self.total = total
        self.list = list
    }
}

/// 打卡点经纬度与地址区域。
struct PunchPointRegion: Codable, Sendable, Equatable, Hashable {
    let lat: Double
    let lot: Double
    let address: String
    let scenicSpotString: String?

    enum CodingKeys: String, CodingKey {
        case lat
        case lot
        case address
        case scenicSpotString = "scenic_spot_str"
    }

    init(lat: Double = 0, lot: Double = 0, address: String = "", scenicSpotString: String? = nil) {
        self.lat = lat
        self.lot = lot
        self.address = address
        self.scenicSpotString = scenicSpotString
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        lat = try container.decodeIfPresent(Double.self, forKey: .lat) ?? 0
        lot = try container.decodeIfPresent(Double.self, forKey: .lot) ?? 0
        address = try container.decodeIfPresent(String.self, forKey: .address) ?? ""
        scenicSpotString = try container.decodeIfPresent(String.self, forKey: .scenicSpotString)
    }
}

/// 打卡点列表条目，对齐 Android `ScenicSpotItem`。
struct PunchPointItem: Decodable, Sendable, Equatable, Hashable, Identifiable {
    let id: Int64
    let name: String
    let status: Int
    let statusLabel: String
    let region: PunchPointRegion?
    let scenicSpotString: String
    let guideImages: [String]
    let mpQrcode: String?
    let createdAt: String
    let creatorId: Int64?
    let creator: String?
    let creatorPhone: String?
    let auditor: String?
    let auditTime: String?
    let auditRemark: String?

    enum CodingKeys: String, CodingKey {
        case id
        case name
        case status
        case statusLabel = "status_label"
        case region
        case scenicSpotString = "scenic_spot_str"
        case guideImages = "guide_imgs"
        case mpQrcode = "mp_qrcode"
        case createdAt = "created_at"
        case creatorId = "creator_id"
        case creator
        case creatorPhone = "creator_phone"
        case auditor
        case auditTime = "audit_time"
        case auditRemark = "audit_remark"
    }

    init(
        id: Int64 = 0,
        name: String = "",
        status: Int = 0,
        statusLabel: String = "",
        region: PunchPointRegion? = nil,
        scenicSpotString: String = "",
        guideImages: [String] = [],
        mpQrcode: String? = nil,
        createdAt: String = "",
        creatorId: Int64? = nil,
        creator: String? = nil,
        creatorPhone: String? = nil,
        auditor: String? = nil,
        auditTime: String? = nil,
        auditRemark: String? = nil
    ) {
        self.id = id
        self.name = name
        self.status = status
        self.statusLabel = statusLabel
        self.region = region
        self.scenicSpotString = scenicSpotString
        self.guideImages = guideImages
        self.mpQrcode = mpQrcode
        self.createdAt = createdAt
        self.creatorId = creatorId
        self.creator = creator
        self.creatorPhone = creatorPhone
        self.auditor = auditor
        self.auditTime = auditTime
        self.auditRemark = auditRemark
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decodeFlexibleInt64(forKey: .id) ?? 0
        name = try container.decodeIfPresent(String.self, forKey: .name) ?? ""
        status = try container.decodeIfPresent(Int.self, forKey: .status) ?? 0
        statusLabel = try container.decodeIfPresent(String.self, forKey: .statusLabel) ?? ""
        region = try container.decodeIfPresent(PunchPointRegion.self, forKey: .region)
        scenicSpotString = try container.decodeIfPresent(String.self, forKey: .scenicSpotString) ?? ""
        guideImages = try container.decodeIfPresent([String].self, forKey: .guideImages) ?? []
        mpQrcode = try container.decodeIfPresent(String.self, forKey: .mpQrcode)
        createdAt = try container.decodeIfPresent(String.self, forKey: .createdAt) ?? ""
        creatorId = try container.decodeFlexibleInt64(forKey: .creatorId)
        creator = try container.decodeIfPresent(String.self, forKey: .creator)
        creatorPhone = try container.decodeIfPresent(String.self, forKey: .creatorPhone)
        auditor = try container.decodeIfPresent(String.self, forKey: .auditor)
        auditTime = try container.decodeIfPresent(String.self, forKey: .auditTime)
        auditRemark = try container.decodeIfPresent(String.self, forKey: .auditRemark)
    }

    /// 列表或详情展示地址。
    var displayAddress: String {
        let address = region?.address.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
        if !address.isEmpty { return address }
        return scenicSpotString.trimmingCharacters(in: .whitespacesAndNewlines)
    }
}

/// 打卡点详情响应，对齐 Android `ScenicSpotDetailResponse`。
struct PunchPointDetail: Decodable, Sendable, Equatable {
    let id: Int64
    let name: String
    let status: Int
    let statusLabel: String
    let region: PunchPointRegion?
    let scenicSpotString: String
    let description: String?
    let guideImages: [String]
    let mpQrcode: String?
    let createdAt: String
    let creatorId: Int64?
    let creator: String?
    let creatorPhone: String?
    let auditor: String?
    let auditTime: String?
    let auditRemark: String?

    enum CodingKeys: String, CodingKey {
        case id
        case name
        case status
        case statusLabel = "status_label"
        case region
        case scenicSpotString = "scenic_spot_str"
        case description
        case guideImages = "guide_imgs"
        case mpQrcode = "mp_qrcode"
        case createdAt = "created_at"
        case creatorId = "creator_id"
        case creator
        case creatorPhone = "creator_phone"
        case auditor
        case auditTime = "audit_time"
        case auditRemark = "audit_remark"
    }

    init(
        id: Int64 = 0,
        name: String = "",
        status: Int = 0,
        statusLabel: String = "",
        region: PunchPointRegion? = nil,
        scenicSpotString: String = "",
        description: String? = nil,
        guideImages: [String] = [],
        mpQrcode: String? = nil,
        createdAt: String = "",
        creatorId: Int64? = nil,
        creator: String? = nil,
        creatorPhone: String? = nil,
        auditor: String? = nil,
        auditTime: String? = nil,
        auditRemark: String? = nil
    ) {
        self.id = id
        self.name = name
        self.status = status
        self.statusLabel = statusLabel
        self.region = region
        self.scenicSpotString = scenicSpotString
        self.description = description
        self.guideImages = guideImages
        self.mpQrcode = mpQrcode
        self.createdAt = createdAt
        self.creatorId = creatorId
        self.creator = creator
        self.creatorPhone = creatorPhone
        self.auditor = auditor
        self.auditTime = auditTime
        self.auditRemark = auditRemark
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decodeFlexibleInt64(forKey: .id) ?? 0
        name = try container.decodeIfPresent(String.self, forKey: .name) ?? ""
        status = try container.decodeIfPresent(Int.self, forKey: .status) ?? 0
        statusLabel = try container.decodeIfPresent(String.self, forKey: .statusLabel) ?? ""
        region = try container.decodeIfPresent(PunchPointRegion.self, forKey: .region)
        scenicSpotString = try container.decodeIfPresent(String.self, forKey: .scenicSpotString) ?? ""
        description = try container.decodeIfPresent(String.self, forKey: .description)
        guideImages = try container.decodeIfPresent([String].self, forKey: .guideImages) ?? []
        mpQrcode = try container.decodeIfPresent(String.self, forKey: .mpQrcode)
        createdAt = try container.decodeIfPresent(String.self, forKey: .createdAt) ?? ""
        creatorId = try container.decodeFlexibleInt64(forKey: .creatorId)
        creator = try container.decodeIfPresent(String.self, forKey: .creator)
        creatorPhone = try container.decodeIfPresent(String.self, forKey: .creatorPhone)
        auditor = try container.decodeIfPresent(String.self, forKey: .auditor)
        auditTime = try container.decodeIfPresent(String.self, forKey: .auditTime)
        auditRemark = try container.decodeIfPresent(String.self, forKey: .auditRemark)
    }

    /// 详情展示地址。
    var displayAddress: String {
        let address = region?.address.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
        if !address.isEmpty { return address }
        return scenicSpotString.trimmingCharacters(in: .whitespacesAndNewlines)
    }

    /// 编辑页初始化坐标。
    var coordinate: CLLocationCoordinate2D? {
        guard let region else { return nil }
        return CLLocationCoordinate2D(latitude: region.lat, longitude: region.lot)
    }
}

/// 新建或编辑打卡点请求体。
struct PunchPointSaveRequest: Encodable, Sendable, Equatable {
    let id: Int64?
    let scenicAreaId: String
    let name: String
    let description: String?
    let region: PunchPointSaveRegion
    let scenicSpotString: String
    let guideImages: [String]

    enum CodingKeys: String, CodingKey {
        case id
        case scenicAreaId = "scenic_area_id"
        case name
        case description
        case region
        case scenicSpotString = "scenic_spot_str"
        case guideImages = "guide_imgs"
    }

    init(
        id: Int64? = nil,
        scenicAreaId: String,
        name: String,
        description: String?,
        region: PunchPointSaveRegion,
        scenicSpotString: String,
        guideImages: [String]
    ) {
        self.id = id
        self.scenicAreaId = scenicAreaId
        self.name = name
        self.description = description
        self.region = region
        self.scenicSpotString = scenicSpotString
        self.guideImages = guideImages
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encodeIfPresent(id, forKey: .id)
        try container.encode(scenicAreaId, forKey: .scenicAreaId)
        try container.encode(name, forKey: .name)
        try container.encodeIfPresent(description, forKey: .description)
        try container.encode(region, forKey: .region)
        try container.encode(scenicSpotString, forKey: .scenicSpotString)
        try container.encode(guideImages, forKey: .guideImages)
    }
}

/// 新建或编辑打卡点中的坐标请求体。
struct PunchPointSaveRegion: Encodable, Sendable, Equatable {
    let lot: Double
    let lat: Double
    let address: String
}

/// 删除打卡点请求体。
struct PunchPointIDRequest: Encodable, Sendable, Equatable {
    let id: Int64
}

/// 打卡点图片上传状态。
struct PunchPointImageState: Sendable, Equatable, Hashable, Identifiable {
    let id: UUID
    var data: Data
    var previewURL: String?
    var fileName: String
    var uploadedURL: String
    var isUploading: Bool
    var uploadProgress: Int
    var errorMessage: String?

    init(
        id: UUID = UUID(),
        data: Data,
        previewURL: String? = nil,
        fileName: String,
        uploadedURL: String = "",
        isUploading: Bool = false,
        uploadProgress: Int = 0,
        errorMessage: String? = nil
    ) {
        self.id = id
        self.data = data
        self.previewURL = previewURL
        self.fileName = fileName
        self.uploadedURL = uploadedURL
        self.isUploading = isUploading
        self.uploadProgress = uploadProgress
        self.errorMessage = errorMessage
    }

    /// 已上传网络图片状态。
    static func remote(url: String) -> PunchPointImageState {
        PunchPointImageState(
            data: Data(),
            previewURL: url,
            fileName: URL(string: url)?.lastPathComponent.nonEmptyTrimmed ?? "punch_point.jpg",
            uploadedURL: url,
            isUploading: false,
            uploadProgress: 100
        )
    }

    /// 是否已经上传完成。
    var isUploaded: Bool { !uploadedURL.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty }
}

/// 打卡点上传进度弹窗状态。
struct PunchPointUploadDialogState: Sendable, Equatable {
    let title: String
    let progress: Int
}

/// 打卡点状态标签的文案与配色。
struct PunchPointStatusPresentation: Sendable, Equatable {
    let title: String
    let backgroundColor: UInt
    let textColor: UInt
}

/// 打卡点 UI 文案与格式化工具。
enum PunchPointDisplayFormatter {
    /// 手机号脱敏。
    static func maskedPhone(_ phone: String?) -> String {
        guard let phone = phone?.trimmingCharacters(in: .whitespacesAndNewlines), !phone.isEmpty else { return "--" }
        guard phone.count >= 11 else { return phone }
        let prefix = phone.prefix(3)
        let suffix = phone.suffix(phone.count - 7)
        return "\(prefix)****\(suffix)"
    }

    /// 运营状态标签，严格对应 Android `OperatingTag`。
    static func operatingStatus(_ status: Int) -> PunchPointStatusPresentation {
        switch status {
        case 1:
            return PunchPointStatusPresentation(title: "营运中", backgroundColor: 0xF0FDF4, textColor: 0x22C55E)
        case 2:
            return PunchPointStatusPresentation(title: "已暂停", backgroundColor: 0xF4F4F4, textColor: 0x4B5563)
        default:
            return PunchPointStatusPresentation(title: "未上线", backgroundColor: 0xF4F4F4, textColor: 0x4B5563)
        }
    }

    /// 审核状态标签，严格对应 Android `StatusLabelChip`。
    static func auditStatus(_ status: Int) -> PunchPointStatusPresentation {
        switch status {
        case 1, 2:
            return PunchPointStatusPresentation(title: "已通过", backgroundColor: 0xF0FDF4, textColor: 0x22C55E)
        case 4:
            return PunchPointStatusPresentation(title: "已驳回", backgroundColor: 0xFFE7E7, textColor: 0xEF4444)
        default:
            return PunchPointStatusPresentation(title: "待审核", backgroundColor: 0xFFF0E2, textColor: 0xFF7B00)
        }
    }
}

private extension KeyedDecodingContainer {
    func decodeFlexibleInt64(forKey key: Key) throws -> Int64? {
        if let value = try? decodeIfPresent(Int64.self, forKey: key) {
            return value
        }
        if let value = try? decodeIfPresent(Int.self, forKey: key) {
            return Int64(value)
        }
        if let value = try? decodeIfPresent(String.self, forKey: key) {
            return Int64(value)
        }
        return nil
    }
}

private extension String {
    var nonEmptyTrimmed: String? {
        let text = trimmingCharacters(in: .whitespacesAndNewlines)
        return text.isEmpty ? nil : text
    }
}
