//
//  ScenicPermissionModels.swift
//  suixinkan
//
//  Created by Codex on 2026/6/23.
//

import Foundation

/// 景区申请地区节点实体，表示省市区树中的一个可选节点。
struct ScenicAreaNode: Decodable, Equatable, Identifiable {
    let id: String
    let code: String
    let name: String
    let children: [ScenicAreaNode]

    enum CodingKeys: String, CodingKey {
        case id
        case code
        case name
        case children
    }

    /// 创建地区节点，主要用于单元测试和本地构造。
    init(id: String, code: String = "", name: String, children: [ScenicAreaNode] = []) {
        self.id = id
        self.code = code
        self.name = name
        self.children = children
    }

    /// 自定义解码，兼容后端 id/code 为数字或字符串。
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        code = try container.decodeLossyString(forKey: .code)
        let rawId = try container.decodeLossyString(forKey: .id).trimmingCharacters(in: .whitespacesAndNewlines)
        id = rawId.isEmpty ? code : rawId
        name = try container.decodeLossyString(forKey: .name)
        children = try container.decodeIfPresent([ScenicAreaNode].self, forKey: .children) ?? []
    }
}

/// 景区申请待审核列表响应实体，表示当前用户提交过的景区入驻申请。
struct ScenicApplicationPendingsResponse: Decodable, Equatable {
    let items: [ScenicApplicationPendingResponse]

    enum CodingKeys: String, CodingKey {
        case items
        case list
        case data
    }

    /// 创建待审核列表响应实体，主要用于测试替身。
    init(items: [ScenicApplicationPendingResponse] = []) {
        self.items = items
    }

    /// 自定义解码，兼容后端返回 items/list/data 三种列表字段。
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        items = try container.decodeIfPresent([ScenicApplicationPendingResponse].self, forKey: .items)
            ?? container.decodeIfPresent([ScenicApplicationPendingResponse].self, forKey: .list)
            ?? container.decodeIfPresent([ScenicApplicationPendingResponse].self, forKey: .data)
            ?? []
    }
}

/// 景区申请待审核实体，表示新增景区申请的审核状态和表单快照。
struct ScenicApplicationPendingResponse: Decodable, Equatable, Identifiable {
    let id: Int
    let code: String
    let scenicId: Int
    let scenicName: String
    let scenicImages: String
    let scenicProvince: String
    let scenicCity: String
    let coopType: Int
    let remark: String?
    let status: Int
    let rejectReason: String?
    let auditedBy: String?
    let auditedAt: String?
    let auditNote: String?
    let createdAt: String

    enum CodingKeys: String, CodingKey {
        case id
        case code
        case scenicId = "scenic_id"
        case scenicName = "scenic_name"
        case scenicImages = "scenic_images"
        case scenicProvince = "scenic_province"
        case scenicCity = "scenic_city"
        case coopType = "coop_type"
        case remark
        case status
        case rejectReason = "reject_reason"
        case auditedBy = "audited_by"
        case auditedAt = "audited_at"
        case auditNote = "audit_note"
        case createdAt = "created_at"
    }

    /// 创建景区申请待审核实体，主要用于测试和本地回填。
    init(
        id: Int,
        code: String = "",
        scenicId: Int = 0,
        scenicName: String,
        scenicImages: String = "",
        scenicProvince: String = "",
        scenicCity: String = "",
        coopType: Int = 1,
        remark: String? = nil,
        status: Int = 1,
        rejectReason: String? = nil,
        auditedBy: String? = nil,
        auditedAt: String? = nil,
        auditNote: String? = nil,
        createdAt: String = ""
    ) {
        self.id = id
        self.code = code
        self.scenicId = scenicId
        self.scenicName = scenicName
        self.scenicImages = scenicImages
        self.scenicProvince = scenicProvince
        self.scenicCity = scenicCity
        self.coopType = coopType
        self.remark = remark
        self.status = status
        self.rejectReason = rejectReason
        self.auditedBy = auditedBy
        self.auditedAt = auditedAt
        self.auditNote = auditNote
        self.createdAt = createdAt
    }

    /// 自定义解码，兼容后端数字和字符串混用。
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decodeLossyInt(forKey: .id) ?? 0
        code = try container.decodeLossyString(forKey: .code)
        scenicId = try container.decodeLossyInt(forKey: .scenicId) ?? 0
        scenicName = try container.decodeLossyString(forKey: .scenicName)
        if let images = try? container.decode([String].self, forKey: .scenicImages) {
            scenicImages = images.joined(separator: ",")
        } else {
            scenicImages = try container.decodeLossyString(forKey: .scenicImages)
        }
        scenicProvince = try container.decodeLossyString(forKey: .scenicProvince)
        scenicCity = try container.decodeLossyString(forKey: .scenicCity)
        coopType = try container.decodeLossyInt(forKey: .coopType) ?? 1
        remark = try? container.decodeIfPresent(String.self, forKey: .remark)
        status = try container.decodeLossyInt(forKey: .status) ?? 0
        rejectReason = try? container.decodeIfPresent(String.self, forKey: .rejectReason)
        auditedBy = try? container.decodeIfPresent(String.self, forKey: .auditedBy)
        auditedAt = try? container.decodeIfPresent(String.self, forKey: .auditedAt)
        auditNote = try? container.decodeIfPresent(String.self, forKey: .auditNote)
        createdAt = try container.decodeLossyString(forKey: .createdAt)
    }
}

/// 景区申请提交请求实体，表示新增景区入驻申请表单。
struct ScenicApplicationSubmitRequest: Encodable, Equatable {
    let scenicName: String?
    let scenicImages: [String]?
    let scenicProvince: String
    let scenicCity: String
    let coopType: Int
    let remark: String
    let scenicId: Int

    enum CodingKeys: String, CodingKey {
        case scenicName = "scenic_name"
        case scenicImages = "scenic_images"
        case scenicProvince = "scenic_province"
        case scenicCity = "scenic_city"
        case coopType = "coop_type"
        case remark
        case scenicId = "scenic_id"
    }
}

/// 角色权限申请实体，表示一条角色/景区权限申请状态。
struct RoleApplyPendingResponse: Decodable, Equatable, Hashable, Identifiable {
    let id: Int
    let code: String
    let roleId: Int
    let roleName: String
    let scenicList: [RoleApplyScenicItem]
    let status: Int
    let statusLabel: String
    let createdAt: String
    let auditedBy: String?
    let auditedAt: String?
    let auditNote: String?

    enum CodingKeys: String, CodingKey {
        case id
        case code
        case roleId = "role_id"
        case roleName = "role_name"
        case scenicList = "scenic_list"
        case status
        case statusLabel = "status_label"
        case createdAt = "created_at"
        case auditedBy = "audited_by"
        case auditedAt = "audited_at"
        case auditNote = "audit_note"
    }

    /// 创建角色权限申请实体，主要用于测试和状态页回填。
    init(
        id: Int,
        code: String = "",
        roleId: Int,
        roleName: String,
        scenicList: [RoleApplyScenicItem] = [],
        status: Int = 1,
        statusLabel: String = "审核中",
        createdAt: String = "",
        auditedBy: String? = nil,
        auditedAt: String? = nil,
        auditNote: String? = nil
    ) {
        self.id = id
        self.code = code
        self.roleId = roleId
        self.roleName = roleName
        self.scenicList = scenicList
        self.status = status
        self.statusLabel = statusLabel
        self.createdAt = createdAt
        self.auditedBy = auditedBy
        self.auditedAt = auditedAt
        self.auditNote = auditNote
    }

    /// 自定义解码，兼容后端字段类型不稳定。
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decodeLossyInt(forKey: .id) ?? 0
        code = try container.decodeLossyString(forKey: .code)
        roleId = try container.decodeLossyInt(forKey: .roleId) ?? 0
        roleName = try container.decodeLossyString(forKey: .roleName)
        scenicList = try container.decodeIfPresent([RoleApplyScenicItem].self, forKey: .scenicList) ?? []
        status = try container.decodeLossyInt(forKey: .status) ?? 0
        statusLabel = try container.decodeLossyString(forKey: .statusLabel)
        createdAt = try container.decodeLossyString(forKey: .createdAt)
        auditedBy = try? container.decodeIfPresent(String.self, forKey: .auditedBy)
        auditedAt = try? container.decodeIfPresent(String.self, forKey: .auditedAt)
        auditNote = try? container.decodeIfPresent(String.self, forKey: .auditNote)
    }
}

/// 角色权限申请景区实体，表示申请中勾选的一个景区。
struct RoleApplyScenicItem: Decodable, Equatable, Hashable, Identifiable {
    let id: Int
    let name: String

    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 RoleApplySubmitRequest: Encodable, Equatable {
    let scenicId: [Int]
    let roleId: Int

    enum CodingKeys: String, CodingKey {
        case scenicId = "scenic_id"
        case roleId = "role_id"
    }
}

/// 上传占位请求实体，表示景区申请已上传文件的轻量元信息。
struct ScenicApplicationUploadPlaceholder: Encodable, Equatable {
    let fileName: String
    let fileType: String
    let fileSize: Int64

    enum CodingKeys: String, CodingKey {
        case fileName = "file_name"
        case fileType = "file_type"
        case fileSize = "file_size"
    }
}

private extension KeyedDecodingContainer {
    /// 宽松解码字符串，兼容后端数字、布尔值和空值。
    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 ""
    }

    /// 宽松解码整数，兼容后端字符串和浮点数。
    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
    }
}
