//
//  AccountContextModels.swift
//  suixinkan
//
//  Created by Codex on 2026/6/22.
//

import Foundation

/// 角色权限响应实体，表示一个角色、该角色权限树以及可访问景区。
struct RolePermissionResponse: Decodable, Equatable {
    let role: RoleInfo
    let scenic: [ScenicInfo]
}

/// 角色实体，表示用户可切换的业务角色及其权限树。
struct RoleInfo: Decodable, Equatable, Identifiable {
    let id: Int
    let name: String
    let notes: String?
    let permission: [PermissionItem]

    /// 角色响应的 JSON 字段映射。
    enum CodingKeys: String, CodingKey {
        case id
        case name
        case notes
        case permission
    }

    /// 创建角色实体，主要用于测试和本地状态恢复。
    init(id: Int, name: String, notes: String? = nil, permission: [PermissionItem] = []) {
        self.id = id
        self.name = name
        self.notes = notes
        self.permission = permission
    }

    /// 自定义解码，兼容 ID 类型不稳定和权限树缺失。
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decodeLossyInt(forKey: .id) ?? 0
        name = try container.decodeLossyString(forKey: .name)
        notes = try container.decodeIfPresent(String.self, forKey: .notes)
        permission = try container.decodeIfPresent([PermissionItem].self, forKey: .permission) ?? []
    }
}

/// 权限节点实体，表示一个菜单或功能权限，并可递归包含子权限。
struct PermissionItem: Decodable, Equatable, Identifiable {
    let id: Int
    let pid: Int
    let name: String
    let uri: String
    let iconSrc: String?
    let children: [PermissionItem]

    /// 权限节点响应的 JSON 字段映射。
    enum CodingKeys: String, CodingKey {
        case id
        case pid
        case name
        case uri
        case iconSrc = "icon_src"
        case children
    }

    /// 创建权限节点，主要用于测试和本地构造权限树。
    init(id: Int, pid: Int = 0, name: String, uri: String = "", iconSrc: String? = nil, children: [PermissionItem] = []) {
        self.id = id
        self.pid = pid
        self.name = name
        self.uri = uri
        self.iconSrc = iconSrc
        self.children = children
    }

    /// 自定义解码，兼容 ID 类型不稳定和 children 缺失。
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decodeLossyInt(forKey: .id) ?? 0
        pid = try container.decodeLossyInt(forKey: .pid) ?? 0
        name = try container.decodeLossyString(forKey: .name)
        uri = try container.decodeLossyString(forKey: .uri)
        iconSrc = try container.decodeIfPresent(String.self, forKey: .iconSrc)
        children = try container.decodeIfPresent([PermissionItem].self, forKey: .children) ?? []
    }
}

/// 景区权限实体，表示某个角色可访问的景区。
struct ScenicInfo: Decodable, Equatable, Identifiable {
    let id: Int
    let name: String
    let status: Int?
    let location: ScenicLocationInfo?
    let coverImg: String?

    /// 景区权限响应的 JSON 字段映射。
    enum CodingKeys: String, CodingKey {
        case id
        case name
        case status
        case location
        case coverImg = "cover_img"
    }

    /// 创建景区权限实体，主要用于测试和兜底列表生成。
    init(id: Int, name: String, status: Int? = nil, location: ScenicLocationInfo? = nil, coverImg: String? = nil) {
        self.id = id
        self.name = name
        self.status = status
        self.location = location
        self.coverImg = coverImg
    }

    /// 自定义解码，兼容 location 是对象或 JSON 字符串。
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decodeLossyInt(forKey: .id) ?? 0
        name = try container.decodeLossyString(forKey: .name)
        status = try container.decodeLossyInt(forKey: .status)
        coverImg = try container.decodeIfPresent(String.self, forKey: .coverImg)
        if let value = try? container.decodeIfPresent(ScenicLocationInfo.self, forKey: .location) {
            location = value
        } else if let rawValue = try? container.decodeIfPresent(String.self, forKey: .location),
                  let data = rawValue.data(using: .utf8) {
            location = try? JSONDecoder().decode(ScenicLocationInfo.self, from: data)
        } else {
            location = nil
        }
    }
}

/// 景区位置实体，表示景区经纬度和地址。
struct ScenicLocationInfo: Decodable, Equatable {
    let lng: Double
    let lat: Double
    let address: String

    /// 景区位置响应的 JSON 字段映射。
    enum CodingKeys: String, CodingKey {
        case lng
        case lat
        case address
    }

    /// 创建景区位置实体，主要用于测试和本地构造。
    init(lng: Double, lat: Double, address: String) {
        self.lng = lng
        self.lat = lat
        self.address = address
    }

    /// 自定义解码，兼容经纬度是字符串或数字。
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        lng = try container.decodeLossyDouble(forKey: .lng) ?? 0
        lat = try container.decodeLossyDouble(forKey: .lat) ?? 0
        address = try container.decodeLossyString(forKey: .address)
    }
}

/// 景区列表响应实体，表示用户可选择的景区列表。
struct ScenicListAllResponse: Decodable, Equatable {
    let total: Int
    let list: [ScenicListItem]

    /// 景区列表响应的 JSON 字段映射。
    enum CodingKeys: String, CodingKey {
        case total
        case list
    }

    /// 创建景区列表响应，主要用于测试和兜底数据。
    init(total: Int, list: [ScenicListItem]) {
        self.total = total
        self.list = list
    }

    /// 自定义解码，兼容 total 类型不稳定。
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        total = try container.decodeLossyInt(forKey: .total) ?? 0
        list = try container.decodeIfPresent([ScenicListItem].self, forKey: .list) ?? []
    }
}

/// 景区列表项实体，表示一个可进入的景区。
struct ScenicListItem: Decodable, Equatable, Identifiable {
    let id: Int
    let name: String

    /// 景区列表项响应的 JSON 字段映射。
    enum CodingKeys: String, CodingKey {
        case id
        case name
    }

    /// 创建景区列表项，主要用于测试和本地构造。
    init(id: Int, name: String) {
        self.id = id
        self.name = name
    }

    /// 自定义解码，兼容 ID 类型不稳定。
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decodeLossyInt(forKey: .id) ?? 0
        name = try container.decodeLossyString(forKey: .name)
    }
}

/// 门店实体，表示一个景区下可进入的门店。
struct StoreItem: Decodable, Equatable, Identifiable {
    let id: Int
    let scenicId: Int
    let name: String
    let address: String
    let status: Int
    let statusText: String
    let tel: String?
    let logo: String?

    /// 门店响应的 JSON 字段映射。
    enum CodingKeys: String, CodingKey {
        case id
        case scenicId = "scenic_id"
        case name
        case address
        case status
        case statusText = "status_text"
        case tel
        case logo
    }

    /// 创建门店实体，主要用于测试和本地构造。
    init(
        id: Int,
        scenicId: Int,
        name: String,
        address: String = "",
        status: Int = 0,
        statusText: String = "",
        tel: String? = nil,
        logo: String? = nil
    ) {
        self.id = id
        self.scenicId = scenicId
        self.name = name
        self.address = address
        self.status = status
        self.statusText = statusText
        self.tel = tel
        self.logo = logo
    }

    /// 自定义解码，兼容字段类型不稳定和可选字段缺失。
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decodeLossyInt(forKey: .id) ?? 0
        scenicId = try container.decodeLossyInt(forKey: .scenicId) ?? 0
        name = try container.decodeLossyString(forKey: .name)
        address = try container.decodeLossyString(forKey: .address)
        status = try container.decodeLossyInt(forKey: .status) ?? 0
        statusText = try container.decodeLossyString(forKey: .statusText)
        tel = try container.decodeIfPresent(String.self, forKey: .tel)
        logo = try container.decodeIfPresent(String.self, forKey: .logo)
    }
}

/// 景点实体，表示某个景区下的景点或打卡点。
struct ScenicSpotItem: Decodable, Equatable, Identifiable {
    let id: Int
    let name: String
    let status: Int
    let statusLabel: String

    /// 景点响应的 JSON 字段映射。
    enum CodingKeys: String, CodingKey {
        case id
        case name
        case status
        case statusLabel = "status_label"
    }

    /// 创建景点实体，主要用于测试和本地构造。
    init(id: Int, name: String, status: Int = 0, statusLabel: String = "") {
        self.id = id
        self.name = name
        self.status = status
        self.statusLabel = statusLabel
    }

    /// 自定义解码，兼容字段类型不稳定。
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decodeLossyInt(forKey: .id) ?? 0
        name = try container.decodeLossyString(forKey: .name)
        status = try container.decodeLossyInt(forKey: .status) ?? 0
        statusLabel = try container.decodeLossyString(forKey: .statusLabel)
    }
}

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 ? "true" : "false"
        }
        return ""
    }

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

    /// 将 Double、Int 或数字字符串宽松解码为浮点数。
    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
    }
}
