//
//  LoginViewModel.swift
//  suixinkan
//
//  Created by Codex on 2026/6/18.
//

import Foundation
import Observation

/// 登录输入焦点实体，标识当前需要聚焦的输入框。
enum LoginField: Hashable {
    case username
    case password
}

/// 登录表单校验错误实体，负责提供用户提示和焦点位置。
enum LoginValidationError: Equatable {
    case invalidPhone
    case emptyPassword
    case privacyUnchecked

    var message: String {
        switch self {
        case .invalidPhone:
            "请输入有效的手机号码"
        case .emptyPassword:
            "请输入密码"
        case .privacyUnchecked:
            "请先阅读并同意相关协议"
        }
    }

    var focusField: LoginField? {
        switch self {
        case .invalidPhone:
            .username
        case .emptyPassword:
            .password
        case .privacyUnchecked:
            nil
        }
    }
}

/// 登录结果实体，区分已完成登录和需要用户选择账号两种情况。
enum LoginResolution {
    case completed(V9AuthResponse)
    case needsAccountSelection(AccountSelectionPayload)
}

/// 登录流程错误实体，表示 token、账号列表或账号 ID 异常。
enum LoginFlowError: LocalizedError {
    case missingToken
    case noAvailableAccount
    case invalidAccount

    var errorDescription: String? {
        switch self {
        case .missingToken:
            "登录响应缺少 token"
        case .noAvailableAccount:
            "当前账号没有可用的景区账号或门店账号，请联系管理员"
        case .invalidAccount:
            "账号信息异常，请重新选择账号"
        }
    }
}

@MainActor
@Observable
/// 登录页 ViewModel，负责表单状态、登录请求和多账号选择流程。
final class LoginViewModel {
    var username = "18651857230"
    var password = "zhifly666"
    var privacyChecked = false
    var isLoading = false
    var isSelectingAccount = false
    var showsPassword = false
    var showsAgreementSheet = false
    var pendingAccountSelection: AccountSelectionPayload?

    var canSubmit: Bool {
        isValidPhone && !trimmedPassword.isEmpty
    }

    var trimmedPassword: String {
        password.trimmingCharacters(in: .whitespacesAndNewlines)
    }

    var normalizedUsername: String {
        normalizedPhoneNumber(username)
    }

    var isValidPhone: Bool {
        normalizedUsername.count == 11 && normalizedUsername.first == "1"
    }

    /// 应用本地登录偏好，只恢复手机号和协议状态，不恢复密码。
    func applyPreferences(_ preferences: LoginPreferences) {
        if username.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty,
           let lastUsername = preferences.lastUsername {
            username = lastUsername
        }
        privacyChecked = preferences.privacyAgreementAccepted
    }

    /// 校验登录表单，并返回第一个需要处理的错误。
    func validateForLogin() -> LoginValidationError? {
        guard isValidPhone else {
            return .invalidPhone
        }
        guard !trimmedPassword.isEmpty else {
            return .emptyPassword
        }
        guard privacyChecked else {
            return .privacyUnchecked
        }
        return nil
    }

    /// 用户同意协议后关闭协议确认弹窗。
    func acceptAgreement() {
        privacyChecked = true
        showsAgreementSheet = false
    }

    /// 当输入包含 +86 前缀时，把手机号规范化为 11 位国内手机号。
    func normalizeUsernameCountryCodeIfNeeded() {
        let digits = username.filter(\.isNumber)
        let normalizedPhone = normalizedPhoneNumber(username)
        guard normalizedPhone != digits, normalizedPhone.count == 11 else { return }
        username = normalizedPhone
    }

    /// 发起登录，并根据账号数量决定直接完成或进入账号选择。
    func login(authAPI: AuthAPI) async throws -> LoginResolution {
        guard !isLoading else {
            throw CancellationError()
        }

        isLoading = true
        defer { isLoading = false }

        let response = try await authAPI.login(
            username: normalizedUsername,
            password: trimmedPassword
        )
        return try await resolveLoginResponse(response, authAPI: authAPI)
    }

    /// 选择一个账号并调用 set-user 换取正式 token。
    func selectAccount(_ account: AccountSwitchAccount, authAPI: AuthAPI) async throws -> V9AuthResponse {
        guard let payload = pendingAccountSelection, payload.hasTempToken else {
            throw LoginFlowError.missingToken
        }
        guard account.businessUserId > 0 else {
            throw LoginFlowError.invalidAccount
        }
        guard !isSelectingAccount else {
            throw CancellationError()
        }

        isSelectingAccount = true
        defer { isSelectingAccount = false }

        let response = try await authAPI.setUser(account.toSetUserRequest(), tokenOverride: payload.tempToken)
        guard !response.token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
            throw LoginFlowError.missingToken
        }
        pendingAccountSelection = nil
        return response
    }

    /// 清空待选账号信息，通常在用户取消账号选择时调用。
    func clearPendingAccountSelection() {
        pendingAccountSelection = nil
    }

    /// 解析登录响应，单账号自动 set-user，多账号保留选择载荷。
    private func resolveLoginResponse(_ response: V9AuthResponse, authAPI: AuthAPI) async throws -> LoginResolution {
        let token = response.token.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !token.isEmpty else {
            throw LoginFlowError.missingToken
        }

        let accounts = response.accounts.filter { $0.businessUserId > 0 }
        guard !accounts.isEmpty else {
            throw LoginFlowError.noAvailableAccount
        }

        if accounts.count == 1, let account = accounts.first {
            let finalResponse = try await authAPI.setUser(account.toSetUserRequest(), tokenOverride: token)
            guard !finalResponse.token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
                throw LoginFlowError.missingToken
            }
            return .completed(finalResponse)
        }

        let payload = AccountSelectionPayload(tempToken: token, accounts: accounts)
        pendingAccountSelection = payload
        return .needsAccountSelection(payload)
    }

    /// 过滤手机号中的非数字字符，并去掉 86 国家码前缀。
    private func normalizedPhoneNumber(_ value: String) -> String {
        var digits = value.filter(\.isNumber)
        if digits.hasPrefix("86"), digits.count == 13 {
            digits.removeFirst(2)
        }
        return digits
    }
}
