//
//  ProfileSpaceViewModel.swift
//  suixinkan
//
//  Created by Codex on 2026/6/29.
//

import Foundation
import Combine

@MainActor
/// 个人空间配置 ViewModel，负责加载资料、维护编辑草稿、校验并提交空间配置。
final class ProfileSpaceViewModel: ObservableObject {
    @Published private(set) var loading = false
    @Published private(set) var saving = false
    @Published var errorMessage: String?
    @Published var nickname = ""
    @Published private(set) var realName = ""
    @Published private(set) var avatarURL = ""
    @Published private(set) var scenicCertification: [String] = []
    @Published var introduction = ""
    @Published var attrLabels: [String] = []
    @Published var shootLabels: [String] = []
    @Published var cameraDevices: [String] = []
    @Published var businessStartTime: Date?
    @Published var businessEndTime: Date?
    @Published var holidayStartTime: Date?
    @Published var holidayEndTime: Date?
    @Published var acceptOrderStatus = 0
    @Published private(set) var scheduleMap: [String: [ScheduleItem]] = [:]
    @Published private(set) var pendingAvatarData: Data?
    @Published private(set) var pendingAvatarFileName: String?
    @Published private(set) var avatarUploadProgress: Int?

    private var originalSnapshot: ProfileSpaceEditableSnapshot?

    /// 当前页面是否存在未保存改动。
    var hasChanges: Bool {
        guard let originalSnapshot else { return pendingAvatarData != nil }
        return editableSnapshot() != originalSnapshot || pendingAvatarData != nil
    }

    /// 使用当前景区加载个人空间配置。
    func load(api: any ProfileSpaceServing, scenicId: Int?) async {
        guard let scenicId else {
            resetForMissingScenic()
            return
        }
        guard !loading else { return }
        loading = true
        errorMessage = nil
        defer { loading = false }
        do {
            let response = try await api.profileSpaceInfo(scenicId: scenicId)
            apply(response)
        } catch {
            errorMessage = error.localizedDescription
        }
    }

    /// 处理用户选择的头像图片，成功后暂存在内存中等待保存。
    func prepareAvatarImage(data: Data, timestamp: TimeInterval = Date().timeIntervalSince1970) throws {
        let processed = try AvatarImageProcessor.process(data: data, timestamp: timestamp)
        pendingAvatarData = processed.data
        pendingAvatarFileName = processed.fileName
    }

    /// 添加个人标签。
    func addAttrLabel(_ label: String) throws {
        attrLabels = try adding(label, to: attrLabels)
    }

    /// 添加拍摄说明标签。
    func addShootLabel(_ label: String) throws {
        shootLabels = try adding(label, to: shootLabels)
    }

    /// 删除个人标签。
    func removeAttrLabel(_ label: String) {
        attrLabels.removeAll { $0 == label }
    }

    /// 删除拍摄说明标签。
    func removeShootLabel(_ label: String) {
        shootLabels.removeAll { $0 == label }
    }

    /// 添加相机设备名称。
    func addCameraDevice(_ device: String) {
        let value = device.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !value.isEmpty else { return }
        cameraDevices.append(value)
    }

    /// 删除相机设备名称。
    func removeCameraDevice(_ device: String) {
        cameraDevices.removeAll { $0 == device }
    }

    /// 保存个人空间配置、昵称和待上传头像。
    func save(api: any ProfileSpaceServing, uploader: any OSSUploadServing, scenicId: Int?) async throws {
        guard let scenicId else {
            errorMessage = ProfileSpaceValidationError.missingScenic.localizedDescription
            throw ProfileSpaceValidationError.missingScenic
        }
        do {
            let request = try makeUpdateRequest(scenicId: scenicId)
            guard !saving else { return }
            saving = true
            errorMessage = nil
            avatarUploadProgress = pendingAvatarData == nil ? nil : 1
            defer {
                saving = false
                avatarUploadProgress = nil
            }

            let uploadedAvatarURL = try await uploadPendingAvatarIfNeeded(uploader: uploader, scenicId: scenicId)
            if let uploadedAvatarURL {
                try await api.updateUserAvatarURL(uploadedAvatarURL)
            }

            let nextNickname = nickname.trimmingCharacters(in: .whitespacesAndNewlines)
            if nextNickname != originalSnapshot?.nickname {
                try await api.updateUserInfo(nickname: nextNickname, password: nil, avatar: nil)
            }

            try await api.updateProfileSpaceInfo(request)
            if let uploadedAvatarURL {
                avatarURL = uploadedAvatarURL
            }
            nickname = nextNickname
            pendingAvatarData = nil
            pendingAvatarFileName = nil
            originalSnapshot = editableSnapshot()
        } catch {
            errorMessage = error.localizedDescription
            throw error
        }
    }

    /// 删除日程并重新加载个人空间配置。
    func deleteSchedule(
        item: ScheduleItem,
        profileAPI: any ProfileSpaceServing,
        scheduleAPI: any ScheduleServing,
        scenicId: Int?
    ) async {
        guard let scenicId else {
            errorMessage = ProfileSpaceValidationError.missingScenic.localizedDescription
            return
        }
        do {
            try await scheduleAPI.deleteSchedule(id: item.id)
            await load(api: profileAPI, scenicId: scenicId)
        } catch {
            errorMessage = error.localizedDescription
        }
    }

    /// 生成全局账号资料兜底，用于保存昵称和头像后同步 AccountContext。
    func accountProfileFallback(_ fallback: AccountProfile?) -> AccountProfile? {
        AccountProfile(
            userId: fallback?.userId ?? "",
            displayName: nickname.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ? (fallback?.displayName ?? "") : nickname,
            phone: fallback?.phone,
            avatarURL: avatarURL.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ? fallback?.avatarURL : avatarURL
        )
    }

    /// 构造空间配置更新请求并执行表单校验。
    func makeUpdateRequest(scenicId: Int) throws -> UpdateProfileSpaceRequest {
        let nextNickname = nickname.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !nextNickname.isEmpty else {
            throw ProfileSpaceValidationError.emptyNickname
        }
        guard let businessStartTime else {
            throw ProfileSpaceValidationError.missingWorkdayStart
        }
        guard let businessEndTime else {
            throw ProfileSpaceValidationError.missingWorkdayEnd
        }
        guard let holidayStartTime else {
            throw ProfileSpaceValidationError.missingHolidayStart
        }
        guard let holidayEndTime else {
            throw ProfileSpaceValidationError.missingHolidayEnd
        }
        guard seconds(in: businessStartTime) < seconds(in: businessEndTime) else {
            throw ProfileSpaceValidationError.invalidWorkdayTime
        }
        guard seconds(in: holidayStartTime) < seconds(in: holidayEndTime) else {
            throw ProfileSpaceValidationError.invalidHolidayTime
        }

        return UpdateProfileSpaceRequest(
            scenicId: "\(scenicId)",
            description: introduction.trimmingCharacters(in: .whitespacesAndNewlines),
            cameraDevice: normalized(cameraDevices),
            attrLabel: normalized(attrLabels),
            shootLabel: normalized(shootLabels),
            businessStartTime: businessStartTime.profileSpaceTimeText,
            businessEndTime: businessEndTime.profileSpaceTimeText,
            holidayStartTime: holidayStartTime.profileSpaceTimeText,
            holidayEndTime: holidayEndTime.profileSpaceTimeText,
            businessRange: [],
            acceptOrderStatus: acceptOrderStatus
        )
    }

    /// 将后端响应写入页面状态。
    private func apply(_ response: ProfileSpaceInfoResponse) {
        realName = response.realName
        nickname = response.nickname
        avatarURL = response.avatar
        scenicCertification = response.scenicCertification
        introduction = response.description
        attrLabels = response.attrLabel
        shootLabels = response.shootLabel
        cameraDevices = response.cameraDevice
        businessStartTime = Date.profileSpaceTime(from: response.businessStartTime)
        businessEndTime = Date.profileSpaceTime(from: response.businessEndTime)
        holidayStartTime = Date.profileSpaceTime(from: response.holidayStartTime)
        holidayEndTime = Date.profileSpaceTime(from: response.holidayEndTime)
        acceptOrderStatus = response.acceptOrderStatus
        scheduleMap = response.schedule
        pendingAvatarData = nil
        pendingAvatarFileName = nil
        originalSnapshot = editableSnapshot()
    }

    /// 缺少景区时清空页面业务状态。
    private func resetForMissingScenic() {
        loading = false
        errorMessage = ProfileSpaceValidationError.missingScenic.localizedDescription
        realName = ""
        nickname = ""
        avatarURL = ""
        scenicCertification = []
        introduction = ""
        attrLabels = []
        shootLabels = []
        cameraDevices = []
        businessStartTime = nil
        businessEndTime = nil
        holidayStartTime = nil
        holidayEndTime = nil
        acceptOrderStatus = 0
        scheduleMap = [:]
        originalSnapshot = nil
    }

    /// 返回当前编辑态快照。
    private func editableSnapshot() -> ProfileSpaceEditableSnapshot {
        ProfileSpaceEditableSnapshot(
            nickname: nickname.trimmingCharacters(in: .whitespacesAndNewlines),
            description: introduction.trimmingCharacters(in: .whitespacesAndNewlines),
            attrLabel: normalized(attrLabels),
            shootLabel: normalized(shootLabels),
            cameraDevice: normalized(cameraDevices),
            businessStartTime: businessStartTime,
            businessEndTime: businessEndTime,
            holidayStartTime: holidayStartTime,
            holidayEndTime: holidayEndTime,
            acceptOrderStatus: acceptOrderStatus,
            avatarURL: avatarURL
        )
    }

    /// 添加标签并执行数量、长度和重复校验。
    private func adding(_ label: String, to labels: [String]) throws -> [String] {
        let value = label.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !value.isEmpty else { throw ProfileSpaceValidationError.emptyLabel }
        guard value.count <= 20 else { throw ProfileSpaceValidationError.labelTooLong }
        guard labels.count < 8 else { throw ProfileSpaceValidationError.labelLimit }
        guard !labels.contains(value) else { throw ProfileSpaceValidationError.duplicateLabel }
        return labels + [value]
    }

    /// 清洗字符串数组，移除空白项。
    private func normalized(_ values: [String]) -> [String] {
        values
            .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
            .filter { !$0.isEmpty }
    }

    /// 返回日期的当天秒数，用于只比较时间部分。
    private func seconds(in date: Date) -> Int {
        let components = Calendar.current.dateComponents([.hour, .minute, .second], from: date)
        return (components.hour ?? 0) * 3600 + (components.minute ?? 0) * 60 + (components.second ?? 0)
    }

    /// 上传待保存头像；没有待上传图片时返回 nil。
    private func uploadPendingAvatarIfNeeded(uploader: any OSSUploadServing, scenicId: Int) async throws -> String? {
        guard let pendingAvatarData else { return nil }
        let fileName = pendingAvatarFileName ?? "avatar_\(Int(Date().timeIntervalSince1970)).jpg"
        return try await uploader.uploadUserAvatar(data: pendingAvatarData, fileName: fileName, scenicId: scenicId) { [weak self] progress in
            Task { @MainActor in
                self?.avatarUploadProgress = progress
            }
        }
    }
}
