//
//  ProfileViewModel.swift
//  suixinkan
//
//  Created by Codex on 2026/6/20.
//

import Foundation
import Observation

@MainActor
@Observable
/// 个人信息页 ViewModel，负责资料加载、编辑状态和提交更新。
final class ProfileViewModel {
    var userInfo: UserInfoResponse?
    var realNameInfo: RealNameInfo?
    var isLoading = false
    var isSaving = false
    var isEditingProfile = false
    var editingNickname = ""
    private(set) var pendingAvatarData: Data?
    private(set) var pendingAvatarFileName: String?
    private(set) var avatarUploadProgress: Int?

    var displayNickname: String {
        nonEmpty(userInfo?.nickname) ?? "未设置昵称"
    }

    var displayRealName: String {
        nonEmpty(userInfo?.realName) ?? "--"
    }

    var displayPhone: String {
        nonEmpty(userInfo?.phone) ?? "--"
    }

    var displayAvatarURL: String {
        nonEmpty(userInfo?.avatar) ?? ""
    }

    var accountStatusText: String {
        nonEmpty(userInfo?.statusName) ?? "--"
    }

    var realNameStatusText: String {
        guard let realNameInfo else { return "点击去实名认证" }
        switch realNameInfo.auditStatus {
        case 2:
            return "已实名认证"
        case 3:
            return "审核不通过"
        default:
            return "审核中"
        }
    }

    /// 重新加载用户资料和实名认证状态。
    func reload(api: ProfileAPI) async throws {
        guard !isLoading else { return }
        isLoading = true
        defer { isLoading = false }

        async let user = api.userInfo()
        async let realName = api.realNameInfo()
        let result = try await (user, realName)

        userInfo = result.0
        realNameInfo = result.1.realNameInfo
    }

    /// 进入昵称编辑状态，并把当前昵称填入编辑框。
    func beginEditing() {
        editingNickname = displayNickname == "未设置昵称" ? "" : displayNickname
        isEditingProfile = true
    }

    /// 退出昵称编辑状态并清空编辑输入。
    func cancelEditing() {
        editingNickname = ""
        isEditingProfile = false
        clearPendingAvatar()
    }

    /// 准备待上传头像，并进入资料编辑状态。
    func prepareAvatarImage(data: Data, timestamp: TimeInterval = Date().timeIntervalSince1970) throws {
        let processed = try AvatarImageProcessor.process(data: data, timestamp: timestamp)
        pendingAvatarData = processed.data
        pendingAvatarFileName = processed.fileName
        if !isEditingProfile {
            beginEditing()
        }
    }

    /// 保存昵称和头像修改，并同步更新本地 userInfo。
    func saveProfile(api: ProfileAPI, uploader: any OSSUploadServing, scenicId: Int) async throws {
        let nextNickname = editingNickname.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !nextNickname.isEmpty else {
            throw ProfileValidationError.emptyNickname
        }

        let nicknameChanged = nextNickname != displayNickname
        let avatarChanged = pendingAvatarData != nil
        guard nicknameChanged || avatarChanged else {
            cancelEditing()
            return
        }

        guard !isSaving else { return }
        isSaving = true
        avatarUploadProgress = avatarChanged ? 1 : nil
        defer {
            isSaving = false
            avatarUploadProgress = nil
        }

        let uploadedAvatarURL = try await uploadPendingAvatarIfNeeded(uploader: uploader, scenicId: scenicId)
        if let uploadedAvatarURL {
            try await api.updateUserAvatarURL(uploadedAvatarURL)
        }
        if nicknameChanged {
            try await api.updateUserInfo(nickname: nextNickname)
        }
        var nextUser = userInfo ?? UserInfoResponse()
        nextUser = UserInfoResponse(
            avatar: uploadedAvatarURL ?? nextUser.avatar,
            realName: nextUser.realName,
            phone: nextUser.phone,
            nickname: nextNickname,
            roleName: nextUser.roleName,
            status: nextUser.status,
            statusName: nextUser.statusName
        )
        userInfo = nextUser
        clearPendingAvatar()
        cancelEditing()
    }

    /// 修改用户密码，并校验最小长度。
    func updatePassword(_ password: String, api: ProfileAPI) async throws {
        let nextPassword = password.trimmingCharacters(in: .whitespacesAndNewlines)
        guard nextPassword.count >= 6 else {
            throw ProfileValidationError.shortPassword
        }

        guard !isSaving else { return }
        isSaving = true
        defer { isSaving = false }

        try await api.updateUserInfo(password: nextPassword)
    }

    /// 将最新 userInfo 合成为全局 AccountProfile，保留已有 userId 等兜底字段。
    func accountProfileFallback(_ fallback: AccountProfile?) -> AccountProfile? {
        guard let userInfo else { return fallback }
        return AccountProfile(
            userId: fallback?.userId ?? "",
            displayName: nonEmpty(userInfo.nickname) ?? nonEmpty(userInfo.realName) ?? fallback?.displayName ?? "未设置昵称",
            phone: nonEmpty(userInfo.phone) ?? fallback?.phone,
            avatarURL: nonEmpty(userInfo.avatar) ?? fallback?.avatarURL
        )
    }

    /// 去除空白字符后返回非空字符串。
    private func nonEmpty(_ value: String?) -> String? {
        let text = value?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
        return text.isEmpty ? nil : text
    }

    /// 上传待保存头像；没有待上传图片时返回 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
            }
        }
    }

    /// 清空本地待上传头像数据。
    private func clearPendingAvatar() {
        pendingAvatarData = nil
        pendingAvatarFileName = nil
        avatarUploadProgress = nil
    }
}

/// 个人信息页校验错误实体，表示昵称和密码输入不符合要求。
enum ProfileValidationError: LocalizedError {
    case emptyNickname
    case shortPassword

    var errorDescription: String? {
        switch self {
        case .emptyNickname:
            "请输入昵称"
        case .shortPassword:
            "密码至少 6 位"
        }
    }
}
