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

import SwiftUI

/// 登录页视图，负责展示登录表单、协议确认和多账号选择入口。
struct LoginView: View {
    @Environment(AppSession.self) private var appSession
    @Environment(AccountContext.self) private var accountContext
    @Environment(PermissionContext.self) private var permissionContext
    @Environment(ToastCenter.self) private var toastCenter
    @Environment(AuthAPI.self) private var authAPI
    @Environment(ProfileAPI.self) private var profileAPI
    @Environment(AccountContextAPI.self) private var accountContextAPI
    @Environment(AuthSessionCoordinator.self) private var authSessionCoordinator
    @Environment(\.globalLoading) private var globalLoading
    @Environment(\.horizontalSizeClass) private var horizontalSizeClass
    @State private var viewModel = LoginViewModel()
    @FocusState private var focusedField: LoginField?

    private var contentMaxWidth: CGFloat {
        horizontalSizeClass == .regular ? 560 : .infinity
    }

    var body: some View {
        NavigationStack {
            GeometryReader { proxy in
                ZStack(alignment: .top) {
                    Image("LoginBackground")
                        .resizable()
                        .scaledToFill()
                        .ignoresSafeArea()
                        .contentShape(Rectangle())
                        .onTapGesture {
                            focusedField = nil
                        }

                    ScrollView(.vertical) {
                        loginContent
                            .frame(maxWidth: contentMaxWidth, alignment: .top)
                            .padding(.horizontal, horizontalPadding(for: proxy.size.width))
                            .padding(.top, topPadding(for: proxy))
                            .padding(.bottom, AppMetrics.Spacing.xxLarge)
                            .frame(maxWidth: .infinity, alignment: .top)
                    }
                    .scrollDismissesKeyboard(.interactively)
                }
            }
            .background(Color(hex: 0x0B1220).ignoresSafeArea())
            .toolbar(.hidden, for: .navigationBar)
            .sheet(isPresented: showsAgreementSheetBinding) {
                LoginAgreementConsentSheet(
                    onOpenAgreement: { title in
                        toastCenter.show("\(title)页面待接入")
                    },
                    onAgreeAndContinue: {
                        viewModel.acceptAgreement()
                        loginAction()
                    }
                )
            }
            .sheet(item: pendingAccountSelectionBinding) { payload in
                AccountSelectionView(
                    payload: payload,
                    isLoading: viewModel.isSelectingAccount,
                    onCancel: {
                        viewModel.clearPendingAccountSelection()
                    },
                    onConfirm: { account in
                        selectAccount(account)
                    }
                )
            }
        }
        .onChange(of: viewModel.username) { _, _ in
            viewModel.normalizeUsernameCountryCodeIfNeeded()
            dismissLoginToastIfNeeded()
        }
        .onChange(of: viewModel.password) { _, _ in
            dismissLoginToastIfNeeded()
        }
        .task {
            viewModel.applyPreferences(authSessionCoordinator.loginPreferences())
        }
    }

    private var loginContent: some View {
        VStack(alignment: .leading, spacing: AppMetrics.Spacing.xxLarge) {
            Text("欢迎使用\n随心瞰商家版")
                .font(.system(size: AppMetrics.FontSize.largeTitle, weight: .semibold))
                .foregroundStyle(.white)
                .lineSpacing(AppMetrics.LineSpacing.title)
                .accessibilityIdentifier("login.title")

            loginCard
        }
    }

    private var loginCard: some View {
        VStack(spacing: 0) {
            VStack(spacing: AppMetrics.Spacing.xSmall) {
                textInput(title: "请输入手机号", text: usernameBinding, icon: "person")
                passwordInput
            }

            Spacer().frame(height: AppMetrics.ControlSize.checkboxTapArea)
            agreementRow
            Spacer().frame(height: AppMetrics.Spacing.mediumLarge)

            Button(action: loginAction) {
                Text("登录")
                    .font(.system(size: AppMetrics.FontSize.body, weight: .medium))
                    .foregroundStyle(.white)
                    .frame(maxWidth: .infinity)
                    .frame(height: AppMetrics.ControlSize.primaryButtonHeight)
                    .background(viewModel.canSubmit ? AppDesign.primary : Color.gray)
                    .clipShape(RoundedRectangle(cornerRadius: AppMetrics.CornerRadius.button))
            }
            .disabled(!viewModel.canSubmit || viewModel.isLoading)
            .accessibilityIdentifier("login.submit")
        }
        .padding(AppMetrics.Spacing.medium)
        .background(.white, in: RoundedRectangle(cornerRadius: AppMetrics.CornerRadius.card))
        .shadow(color: .black.opacity(0.12), radius: 18, x: 0, y: 8)
    }

    /// 计算登录内容相对安全区的顶部距离。
    private func topPadding(for proxy: GeometryProxy) -> CGFloat {
        max(proxy.safeAreaInsets.top + 24, 72)
    }

    /// 根据屏幕宽度计算登录内容的水平边距。
    private func horizontalPadding(for width: CGFloat) -> CGFloat {
        min(max(width * 0.07, 20), 30)
    }

    /// 处理登录按钮点击，完成表单校验并启动登录流程。
    private func loginAction() {
        focusedField = nil

        if let validationError = viewModel.validateForLogin() {
            if validationError == .privacyUnchecked {
                viewModel.showsAgreementSheet = true
            } else {
                toastCenter.show(validationError.message)
                focusedField = validationError.focusField
            }
            return
        }

        Task {
            do {
                try await globalLoading.withLoading(message: "登录中...") {
                    let resolution = try await viewModel.login(authAPI: authAPI)
                    switch resolution {
                    case let .completed(response):
                        await completeLogin(with: response)
                    case .needsAccountSelection:
                        break
                    }
                }
            } catch is CancellationError {
                // Keep cancellation silent; the current task was abandoned by SwiftUI.
            } catch {
                toastCenter.show(error.localizedDescription)
            }
        }
    }

    /// 处理账号选择页的确认动作，并使用所选账号换取正式 token。
    private func selectAccount(_ account: AccountSwitchAccount) {
        Task {
            do {
                try await globalLoading.withLoading(message: "账号切换中...") {
                    let response = try await viewModel.selectAccount(account, authAPI: authAPI)
                    await completeLogin(with: response)
                }
            } catch is CancellationError {
                // Keep cancellation silent; the current task was abandoned by SwiftUI.
            } catch {
                toastCenter.show(error.localizedDescription)
            }
        }
    }

    /// 将最终登录响应写入全局会话和账号上下文。
    private func completeLogin(with response: V9AuthResponse) async {
        do {
            try await authSessionCoordinator.completeLogin(
                with: response,
                username: viewModel.normalizedUsername,
                privacyAgreementAccepted: viewModel.privacyChecked,
                appSession: appSession,
                accountContext: accountContext,
                permissionContext: permissionContext,
                profileAPI: profileAPI,
                accountContextAPI: accountContextAPI
            )
        } catch {
            toastCenter.show(error.localizedDescription)
        }
    }

    /// 输入内容变化后清除当前 Toast，避免旧错误提示停留。
    private func dismissLoginToastIfNeeded() {
        toastCenter.dismiss()
    }

    /// 构造通用文本输入框，当前用于手机号输入。
    private func textInput(title: String, text: Binding<String>, icon: String) -> some View {
        HStack(spacing: AppMetrics.Spacing.xSmall) {
            Image(systemName: icon)
                .font(.system(size: AppMetrics.ControlSize.smallIcon))
                .foregroundStyle(AppDesign.textSecondary)

            TextField(title, text: text)
                .font(.system(size: AppMetrics.FontSize.body))
                .textFieldStyle(.plain)
                .foregroundStyle(AppDesign.textPrimary)
                .tint(AppDesign.primary)
                .keyboardType(.phonePad)
                .textContentType(.telephoneNumber)
                .textInputAutocapitalization(.never)
                .autocorrectionDisabled(true)
                .submitLabel(.next)
                .focused($focusedField, equals: .username)
                .onSubmit {
                    focusedField = .password
                }
                .accessibilityIdentifier("login.username")
        }
        .padding(.horizontal, AppMetrics.Spacing.medium)
        .frame(height: AppMetrics.ControlSize.inputHeight)
        .background(Color(hex: 0xF1F5F9), in: RoundedRectangle(cornerRadius: AppMetrics.CornerRadius.input))
        .overlay(
            RoundedRectangle(cornerRadius: AppMetrics.CornerRadius.input)
                .stroke(Color(hex: 0xDDE3EA), lineWidth: 1)
        )
        .contentShape(Rectangle())
        .onTapGesture {
            focusedField = .username
        }
    }

    private var passwordInput: some View {
        HStack(spacing: AppMetrics.Spacing.xSmall) {
            Image(systemName: "lock")
                .font(.system(size: AppMetrics.ControlSize.smallIcon))
                .foregroundStyle(AppDesign.textSecondary)

            Group {
                if viewModel.showsPassword {
                    TextField("请输入密码", text: passwordBinding)
                } else {
                    SecureField("请输入密码", text: passwordBinding)
                }
            }
            .font(.system(size: AppMetrics.FontSize.body))
            .textFieldStyle(.plain)
            .foregroundStyle(AppDesign.textPrimary)
            .tint(AppDesign.primary)
            .textInputAutocapitalization(.never)
            .autocorrectionDisabled(true)
            .submitLabel(.go)
            .focused($focusedField, equals: .password)
            .onSubmit {
                if viewModel.canSubmit && !viewModel.isLoading {
                    loginAction()
                }
            }

            Button {
                viewModel.showsPassword.toggle()
            } label: {
                Image(viewModel.showsPassword ? "LoginPwdVisible" : "LoginPwdInvisible")
                    .resizable()
                    .frame(width: AppMetrics.ControlSize.passwordIcon, height: AppMetrics.ControlSize.passwordIcon)
                    .frame(width: AppMetrics.ControlSize.iconTapArea, height: AppMetrics.ControlSize.iconTapArea)
            }
            .buttonStyle(.plain)
        }
        .padding(.horizontal, AppMetrics.Spacing.medium)
        .frame(height: AppMetrics.ControlSize.inputHeight)
        .background(Color(hex: 0xF1F5F9), in: RoundedRectangle(cornerRadius: AppMetrics.CornerRadius.input))
        .overlay(
            RoundedRectangle(cornerRadius: AppMetrics.CornerRadius.input)
                .stroke(Color(hex: 0xDDE3EA), lineWidth: 1)
        )
        .contentShape(Rectangle())
        .onTapGesture {
            focusedField = .password
        }
        .accessibilityIdentifier("login.password")
    }

    private var agreementRow: some View {
        HStack(alignment: .top, spacing: AppMetrics.Spacing.xSmall) {
            Button {
                viewModel.privacyChecked.toggle()
            } label: {
                Image(viewModel.privacyChecked ? "LoginCheckboxChecked" : "LoginCheckboxUnchecked")
                    .resizable()
                    .frame(width: AppMetrics.ControlSize.checkboxIcon, height: AppMetrics.ControlSize.checkboxIcon)
                    .frame(width: AppMetrics.ControlSize.checkboxTapArea, height: AppMetrics.ControlSize.checkboxTapArea)
                    .contentShape(Rectangle())
            }
            .buttonStyle(.plain)
            .accessibilityIdentifier("login.privacy")

            agreementText
                .padding(.top, AppMetrics.Spacing.xxSmall)
        }
        .frame(maxWidth: .infinity, alignment: .topLeading)
    }

    private var agreementText: some View {
        ViewThatFits(in: .horizontal) {
            HStack(spacing: 0) {
                agreementPrefix
                userAgreementButton
                agreementSeparator
                privacyPolicyButton
            }

            VStack(alignment: .leading, spacing: AppMetrics.Spacing.xxSmall) {
                HStack(spacing: 0) {
                    agreementPrefix
                    userAgreementButton
                }
                HStack(spacing: 0) {
                    agreementSeparator
                    privacyPolicyButton
                }
            }
        }
        .font(.system(size: AppMetrics.FontSize.subheadline))
    }

    private var agreementPrefix: some View {
        Text("已阅读并同意")
            .foregroundStyle(Color(hex: 0x666666))
    }

    private var agreementSeparator: some View {
        Text("与")
            .foregroundStyle(Color(hex: 0x666666))
    }

    private var userAgreementButton: some View {
        Button("《用户协议》") {
            toastCenter.show("用户协议页面待接入")
        }
        .foregroundStyle(Color(hex: 0x208BFF))
        .accessibilityIdentifier("login.userAgreement")
    }

    private var privacyPolicyButton: some View {
        Button("《隐私政策》") {
            toastCenter.show("隐私政策页面待接入")
        }
        .foregroundStyle(Color(hex: 0x208BFF))
        .accessibilityIdentifier("login.privacyPolicy")
    }

    private var usernameBinding: Binding<String> {
        Binding(
            get: { viewModel.username },
            set: { viewModel.username = $0 }
        )
    }

    private var passwordBinding: Binding<String> {
        Binding(
            get: { viewModel.password },
            set: { viewModel.password = $0 }
        )
    }

    private var showsAgreementSheetBinding: Binding<Bool> {
        Binding(
            get: { viewModel.showsAgreementSheet },
            set: { viewModel.showsAgreementSheet = $0 }
        )
    }

    private var pendingAccountSelectionBinding: Binding<AccountSelectionPayload?> {
        Binding(
            get: { viewModel.pendingAccountSelection },
            set: { viewModel.pendingAccountSelection = $0 }
        )
    }
}

/// 协议确认弹窗，负责在用户未勾选协议时引导确认。
private struct LoginAgreementConsentSheet: View {
    private static let compactDetent = PresentationDetent.height(240)

    let onOpenAgreement: (String) -> Void
    let onAgreeAndContinue: () -> Void

    var body: some View {
        VStack(spacing: AppMetrics.Spacing.sheet) {
            Text("请先阅读并同意相关协议")
                .font(.system(size: AppMetrics.FontSize.title3, weight: .semibold))
                .foregroundStyle(AppDesign.textPrimary)
                .frame(maxWidth: .infinity, alignment: .center)

            agreementDescription

            Button(action: onAgreeAndContinue) {
                Text("同意并继续")
                    .font(.system(size: AppMetrics.FontSize.body, weight: .medium))
                    .foregroundStyle(.white)
                    .frame(maxWidth: .infinity)
                    .frame(height: AppMetrics.ControlSize.sheetButtonHeight)
                    .background(AppDesign.primary, in: RoundedRectangle(cornerRadius: AppMetrics.CornerRadius.button))
            }
            .buttonStyle(.plain)
            .padding(.top, AppMetrics.Spacing.large)
            .accessibilityIdentifier("login.agreement.continue")
        }
        .padding(.horizontal, AppMetrics.Spacing.large)
        .padding(.top, AppMetrics.Spacing.sheet)
        .padding(.bottom, AppMetrics.Spacing.mediumLarge)
        .presentationDetents([Self.compactDetent])
    }

    private var agreementDescription: some View {
        VStack(spacing: AppMetrics.Spacing.small) {
            Text("为了保障你的账号安全和服务体验，请阅读并同意")
                .font(.system(size: AppMetrics.FontSize.subheadline))
                .foregroundStyle(AppDesign.textSecondary)
                .multilineTextAlignment(.center)

            HStack(spacing: 0) {
                agreementToken(title: "用户协议")
                Text("和")
                    .font(.system(size: AppMetrics.FontSize.subheadline))
                    .foregroundStyle(AppDesign.textSecondary)
                agreementToken(title: "隐私政策")
            }
        }
        .frame(maxWidth: .infinity)
    }

    /// 构造协议链接文本按钮。
    private func agreementToken(title: String) -> some View {
        Button {
            onOpenAgreement(title)
        } label: {
            Text("《\(title)》")
                .font(.system(size: AppMetrics.FontSize.subheadline, weight: .medium))
                .foregroundStyle(Color(hex: 0x208BFF))
        }
        .buttonStyle(.plain)
    }
}

#Preview {
    LoginView()
        .environment(AppSession())
        .environment(AccountContext())
        .environment(PermissionContext())
        .environment(ToastCenter())
        .environment(AuthAPI(client: APIClient()))
        .environment(ProfileAPI(client: APIClient()))
        .environment(AccountContextAPI(client: APIClient()))
        .environment(AuthSessionCoordinator())
}
