//
//  PunchPointModels.swift
//  suixinkan
//
//  Created by Codex on 2026/6/24.
//

import Foundation

/// 打卡点筛选实体，表示列表页可选择的审核或运营状态。
enum PunchPointFilter: Int, CaseIterable, Identifiable {
    case all = 0
    case operating = 1
    case paused = 2
    case pendingReview = 3
    case rejected = 4

    var id: Int { rawValue }

    /// 筛选项展示标题。
    var title: String {
        switch self {
        case .all: "全部"
        case .operating: "运营中"
        case .paused: "已暂停"
        case .pendingReview: "待审核"
        case .rejected: "已驳回"
        }
    }
}

/// 打卡点区域实体，表示点位经纬度和地址。
struct PunchPointRegion: Codable, Hashable {
    var lat: Double
    var lot: Double
    var address: String
    var scenicSpotStr: String?

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

    /// 创建打卡点区域实体。
    init(lat: Double, lot: Double, address: String, scenicSpotStr: String? = nil) {
        self.lat = lat
        self.lot = lot
        self.address = address
        self.scenicSpotStr = scenicSpotStr
    }

    /// 宽松解码区域字段，兼容经纬度数字和字符串混合返回。
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        lat = try container.decodeLossyDouble(forKey: .lat) ?? 0
        lot = try container.decodeLossyDouble(forKey: .lot) ?? 0
        address = try container.decodeLossyString(forKey: .address)
        scenicSpotStr = try container.decodeIfPresent(String.self, forKey: .scenicSpotStr)
    }
}

/// 打卡点列表项实体，表示一个景区下可管理的打卡点。
struct PunchPointItem: Decodable, Hashable, Identifiable {
    let id: Int
    let scenicAreaId: Int
    let name: String
    let status: Int
    let statusLabel: String
    let description: String
    let region: PunchPointRegion?
    let scenicSpotStr: String
    let guideImages: [String]
    let mpQrcode: String
    let createdAt: String
    let creator: String
    let creatorPhone: String
    let auditor: String
    let auditTime: String
    let auditRemark: String

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

    /// 创建打卡点列表项，主要用于测试和表单兜底展示。
    init(
        id: Int,
        scenicAreaId: Int = 0,
        name: String,
        status: Int = 0,
        statusLabel: String = "",
        description: String = "",
        region: PunchPointRegion? = nil,
        scenicSpotStr: String = "",
        guideImages: [String] = [],
        mpQrcode: String = "",
        createdAt: String = "",
        creator: String = "",
        creatorPhone: String = "",
        auditor: String = "",
        auditTime: String = "",
        auditRemark: String = ""
    ) {
        self.id = id
        self.scenicAreaId = scenicAreaId
        self.name = name
        self.status = status
        self.statusLabel = statusLabel
        self.description = description
        self.region = region
        self.scenicSpotStr = scenicSpotStr
        self.guideImages = guideImages
        self.mpQrcode = mpQrcode
        self.createdAt = createdAt
        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.decodeLossyInt(forKey: .id) ?? 0
        scenicAreaId = try container.decodeLossyInt(forKey: .scenicAreaId) ?? 0
        name = try container.decodeLossyString(forKey: .name)
        status = try container.decodeLossyInt(forKey: .status) ?? 0
        statusLabel = try container.decodeLossyString(forKey: .statusLabel)
        description = try container.decodeLossyString(forKey: .description)
        region = try container.decodeIfPresent(PunchPointRegion.self, forKey: .region)
        scenicSpotStr = try container.decodeLossyString(forKey: .scenicSpotStr)
        guideImages = (try? container.decodeIfPresent([String].self, forKey: .guideImages)) ?? []
        mpQrcode = try container.decodeLossyString(forKey: .mpQrcode)
        createdAt = try container.decodeLossyString(forKey: .createdAt)
        creator = try container.decodeLossyString(forKey: .creator)
        creatorPhone = try container.decodeLossyString(forKey: .creatorPhone)
        auditor = try container.decodeLossyString(forKey: .auditor)
        auditTime = try container.decodeLossyString(forKey: .auditTime)
        auditRemark = try container.decodeLossyString(forKey: .auditRemark)
    }
}

/// 新增打卡点请求实体，表示提交给后端的点位表单。
struct AddPunchPointRequest: Encodable, Equatable {
    let scenicAreaId: String
    let name: String
    let description: String
    let region: PunchPointRegion
    let scenicSpotStr: String
    let guideImages: [String]

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

/// 编辑打卡点请求实体，表示已有点位的完整修改内容。
struct EditPunchPointRequest: Encodable, Equatable {
    let id: Int
    let scenicAreaId: String
    let name: String
    let description: String
    let region: PunchPointRegion
    let scenicSpotStr: String
    let guideImages: [String]

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

/// 删除打卡点请求实体，表示要删除的点位 ID。
struct PunchPointDeleteRequest: Encodable, Equatable {
    let id: Int
}

/// 打卡点本地待上传图片实体，保存 PhotosPicker 读取后的内存图片。
struct PunchPointLocalImage: Identifiable, Equatable {
    let id = UUID()
    let data: Data
    let fileName: String
    var remoteURL: String?
    var progress: Int

    /// 创建待上传打卡点图片。
    init(data: Data, fileName: String, remoteURL: String? = nil, progress: Int = 0) {
        self.data = data
        self.fileName = fileName
        self.remoteURL = remoteURL
        self.progress = progress
    }
}

private extension KeyedDecodingContainer {
    /// 将 String、数字和 Bool 宽松解码为字符串。
    func decodeLossyString(forKey key: Key) throws -> String {
        if let value = try? decodeIfPresent(String.self, forKey: key) { return value }
        if let value = try? decodeIfPresent(Int.self, forKey: key) { return String(value) }
        if let value = try? decodeIfPresent(Double.self, forKey: key) { return String(value) }
        if let value = try? decodeIfPresent(Bool.self, forKey: key) { return value ? "1" : "0" }
        return ""
    }

    /// 将 String、Double 和 Int 宽松解码为 Int。
    func decodeLossyInt(forKey key: Key) throws -> Int? {
        if let value = try? decodeIfPresent(Int.self, forKey: key) { return value }
        if let value = try? decodeIfPresent(Double.self, forKey: key) { return Int(value) }
        if let value = try? decodeIfPresent(String.self, forKey: key) {
            return Int(value.trimmingCharacters(in: .whitespacesAndNewlines))
        }
        return nil
    }

    /// 将 String、Double 和 Int 宽松解码为 Double。
    func decodeLossyDouble(forKey key: Key) throws -> Double? {
        if let value = try? decodeIfPresent(Double.self, forKey: key) { return value }
        if let value = try? decodeIfPresent(Int.self, forKey: key) { return Double(value) }
        if let value = try? decodeIfPresent(String.self, forKey: key) {
            return Double(value.trimmingCharacters(in: .whitespacesAndNewlines))
        }
        return nil
    }
}
