//
//  HomeViewController.swift
//  suixinkan
//

import SnapKit
import UIKit

/// 首页 Tab 根页面，按身份与权限展示信息与入口。
final class HomeViewController: BaseViewController {

    private static let fallbackCellReuseIdentifier = "HomeFallbackCell"

    private let viewModel = HomeViewModel()
    private let homeAPI = NetworkServices.shared.homeAPI
    private let messageCenterAPI = NetworkServices.shared.messageCenterAPI

    private let scenicHeaderView = HomeScenicHeaderView()
    private var collectionView: UICollectionView!
    private var dataSource: UICollectionViewDiffableDataSource<HomeCollectionSection, HomeCollectionItem>!
    private var currentSections: [HomeCollectionSection] = []

    private var hasInitialized = false
    private var countdownTimer: Timer?
    private var activeDialog: HomeDialogKind?

    override func setupNavigationBar() {
        navigationController?.setNavigationBarHidden(true, animated: false)
    }

    override func setupUI() {
        view.backgroundColor = AppColor.pageBackground

        collectionView = UICollectionView(
            frame: .zero,
            collectionViewLayout: HomeCollectionLayoutBuilder.makeLayout(sections: [.commonApps])
        )
        collectionView.backgroundColor = .clear
//        collectionView.alwaysBounceVertical = true
//        collectionView.alwaysBounceHorizontal = false
//        collectionView.showsHorizontalScrollIndicator = false
//        collectionView.contentInsetAdjustmentBehavior = .never
        // 水平边距由 section.contentInsets 控制，避免 contentInset 与布局宽度不一致导致横向滑动。
        collectionView.contentInset = UIEdgeInsets(
            top: AppSpacing.sm,
            left: 0,
            bottom: AppSpacing.md,
            right: 0
        )
        registerCollectionCells()

        view.addSubview(scenicHeaderView)
        view.addSubview(collectionView)

        dataSource = makeDataSource()
    }

    override func setupConstraints() {
        scenicHeaderView.snp.makeConstraints { make in
            make.top.leading.trailing.equalToSuperview()
            make.bottom.equalTo(view.safeAreaLayoutGuide.snp.top).offset(AppSpacing.homeHeaderHeight)
        }
        collectionView.snp.makeConstraints { make in
            make.top.equalTo(scenicHeaderView.snp.bottom)
            make.leading.trailing.equalToSuperview()
            make.bottom.equalTo(view.safeAreaLayoutGuide)
        }
    }

    override func bindActions() {
        viewModel.onStateChange = { [weak self] in
            Task { @MainActor in self?.applyViewModel() }
        }
        viewModel.onShowMessage = { [weak self] message in
            Task { @MainActor in self?.showToast(message) }
        }
        scenicHeaderView.onTap = { [weak self] in self?.presentScenicSelection() }
        scenicHeaderView.onMessageTap = { [weak self] in self?.openMessageCenter() }
        collectionView.delegate = self

        NotificationCenter.default.addObserver(
            self,
            selector: #selector(handleAccountDidSwitch),
            name: NotificationName.accountDidSwitch,
            object: nil
        )
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(handleScenicDidChange),
            name: NotificationName.scenicDidChange,
            object: nil
        )
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(handleUnreadMessageCountDidChange(_:)),
            name: NotificationName.unreadMessageCountDidChange,
            object: nil
        )
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(handleUnreadMessageCountDidChange(_:)),
            name: UIApplication.didBecomeActiveNotification,
            object: nil
        )
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        navigationController?.setNavigationBarHidden(true, animated: animated)
        if hasInitialized {
            Task { await refreshUnreadMessageStatus() }
        }
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        if !hasInitialized {
            hasInitialized = true
            Task { await initializeHome() }
        } else {
            Task {
                let shouldReevaluateDialogs = viewModel.needsPermissionReload
                await viewModel.reloadIfNeeded(api: homeAPI)
                if shouldReevaluateDialogs {
                    await viewModel.evaluateDialogs(api: homeAPI)
                    applyViewModel()
                }
            }
        }
        startCountdownTimer()
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        countdownTimer?.invalidate()
        countdownTimer = nil
    }

    deinit {
        NotificationCenter.default.removeObserver(self)
    }

    private func registerCollectionCells() {
        collectionView.register(HomeWorkStatusCell.self, forCellWithReuseIdentifier: HomeWorkStatusCell.reuseIdentifier)
        collectionView.register(
            UICollectionViewCell.self,
            forCellWithReuseIdentifier: Self.fallbackCellReuseIdentifier
        )
        collectionView.register(
            HomeLocationReportCell.self,
            forCellWithReuseIdentifier: HomeLocationReportCell.reuseIdentifier
        )
        collectionView.register(
            HomeQuickActionsCell.self,
            forCellWithReuseIdentifier: HomeQuickActionsCell.reuseIdentifier
        )
        collectionView.register(HomeStoreCell.self, forCellWithReuseIdentifier: HomeStoreCell.reuseIdentifier)
        collectionView.register(HomeMenuCell.self, forCellWithReuseIdentifier: HomeMenuCell.reuseIdentifier)
        collectionView.register(
            HomeSectionHeaderView.self,
            forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader,
            withReuseIdentifier: HomeSectionHeaderView.reuseIdentifier
        )
    }

    private func makeDataSource() -> UICollectionViewDiffableDataSource<HomeCollectionSection, HomeCollectionItem> {
        let dataSource = UICollectionViewDiffableDataSource<HomeCollectionSection, HomeCollectionItem>(
            collectionView: collectionView
        ) { [weak self] collectionView, indexPath, item in
            guard let self else {
                return collectionView.dequeueReusableCell(
                    withReuseIdentifier: Self.fallbackCellReuseIdentifier,
                    for: indexPath
                )
            }
            guard indexPath.section < self.currentSections.count else {
                return collectionView.dequeueReusableCell(
                    withReuseIdentifier: Self.fallbackCellReuseIdentifier,
                    for: indexPath
                )
            }
            let section = self.currentSections[indexPath.section]
            switch (section, item) {
            case (.workStatus, .workStatus):
                let cell = collectionView.dequeueReusableCell(
                    withReuseIdentifier: HomeWorkStatusCell.reuseIdentifier,
                    for: indexPath
                ) as! HomeWorkStatusCell
                cell.cardView.onOnlineTap = { [weak self] in
                    self?.viewModel.showOnlineStatusSwitchDialog()
                }
                cell.cardView.onReminderTap = { [weak self] in
                    self?.presentReminderPicker()
                }
                cell.apply(
                    isOnline: self.viewModel.isOnline,
                    countdownText: self.viewModel.countdownDisplayText,
                    reminderMinutes: self.viewModel.reminderMinutes
                )
                return cell

            case (.locationReport, .locationReport):
                let cell = collectionView.dequeueReusableCell(
                    withReuseIdentifier: HomeLocationReportCell.reuseIdentifier,
                    for: indexPath
                ) as! HomeLocationReportCell
                cell.cardView.onReportTap = { [weak self] in
                    guard let self else { return }
                    Task { @MainActor in
                        self.showLoading()
                        defer { self.hideLoading() }
                        await self.viewModel.manualReportLocation(api: self.homeAPI)
                    }
                }
                cell.apply(
                    address: HomeViewModel.locationReportPromptText,
                    isReporting: self.viewModel.isLocationReporting
                )
                return cell

            case (.quickActions, .quickActions):
                let cell = collectionView.dequeueReusableCell(
                    withReuseIdentifier: HomeQuickActionsCell.reuseIdentifier,
                    for: indexPath
                ) as! HomeQuickActionsCell
                cell.cardView.onCollectPayment = { [weak self] in
                    guard AppStore.shared.session.currentScenicId > 0 else {
                        self?.showToast("请先选择景区")
                        return
                    }
                    self?.navigationController?.pushViewController(
                        PaymentCollectionDetailsViewController(),
                        animated: true
                    )
                }
                cell.cardView.onSubmitTask = { [weak self] in
                    self?.navigationController?.pushViewController(TaskAddViewController(), animated: true)
                }
                cell.cardView.onToggleOnline = { [weak self] in
                    self?.viewModel.showOnlineStatusSwitchDialog()
                }
                cell.apply(isOnline: self.viewModel.isOnline)
                return cell

            case (.store, .store):
                let cell = collectionView.dequeueReusableCell(
                    withReuseIdentifier: HomeStoreCell.reuseIdentifier,
                    for: indexPath
                ) as! HomeStoreCell
                if let store = self.viewModel.storeItem {
                    cell.apply(store: store)
                }
                return cell

            case (.commonApps, .menu(let menu)):
                let cell = collectionView.dequeueReusableCell(
                    withReuseIdentifier: HomeMenuCell.reuseIdentifier,
                    for: indexPath
                ) as! HomeMenuCell
                cell.apply(menu: menu)
                return cell

            default:
                return collectionView.dequeueReusableCell(
                    withReuseIdentifier: Self.fallbackCellReuseIdentifier,
                    for: indexPath
                )
            }
        }

        dataSource.supplementaryViewProvider = { collectionView, kind, indexPath in
            guard kind == UICollectionView.elementKindSectionHeader else {
                return nil
            }
            let header = collectionView.dequeueReusableSupplementaryView(
                ofKind: kind,
                withReuseIdentifier: HomeSectionHeaderView.reuseIdentifier,
                for: indexPath
            ) as! HomeSectionHeaderView
            let title = indexPath.section < self.currentSections.count
                && self.currentSections[indexPath.section] == .commonApps ? "常用应用" : ""
            header.apply(title: title)
            return header
        }

        return dataSource
    }

    private func initializeHome() async {
        showLoading()
        await viewModel.initialize(api: homeAPI)
        await refreshUnreadMessageStatus()
        hideLoading()

        applyViewModel()
        await evaluateDialogsWithDelay()
    }

    private func evaluateDialogsWithDelay() async {
        try? await Task.sleep(nanoseconds: 100_000_000)
        await viewModel.evaluateDialogs(api: homeAPI)
        applyViewModel()
        presentDialogsIfNeeded()
    }

    @MainActor
    private func applyViewModel() {
        scenicHeaderView.apply(
            scenicName: viewModel.currentScenicName,
            hasUnreadMessages: viewModel.hasUnreadMessages
        )

        let showWorkBlock = !viewModel.isMinimalTopRole
        let showStore = viewModel.currentAppRole == .storeAdmin && viewModel.storeItem != nil
        let sections = HomeCollectionLayoutBuilder.sectionOrder(
            showWorkBlock: showWorkBlock,
            showStore: showStore,
            storeItem: viewModel.storeItem
        )

        if sections != currentSections {
            currentSections = sections
            collectionView.setCollectionViewLayout(
                HomeCollectionLayoutBuilder.makeLayout(sections: sections),
                animated: false
            )
        }

        let snapshot = HomeCollectionLayoutBuilder.makeSnapshot(
            showWorkBlock: showWorkBlock,
            showStore: showStore,
            storeItem: viewModel.storeItem,
            commonMenus: viewModel.commonMenus
        )
        dataSource.apply(snapshot, animatingDifferences: false) { [weak self] in
            self?.refreshStatusVisibleCells()
        }
        presentDialogsIfNeeded()
    }

    private func refreshStatusVisibleCells() {
        refreshWorkStatusVisibleCell()
        refreshQuickActionsVisibleCell()
    }

    private func refreshWorkStatusVisibleCell() {
        guard !viewModel.isMinimalTopRole,
              let sectionIndex = currentSections.firstIndex(of: .workStatus) else {
            return
        }
        let indexPath = IndexPath(item: 0, section: sectionIndex)
        guard let cell = collectionView.cellForItem(at: indexPath) as? HomeWorkStatusCell else {
            return
        }
        cell.apply(
            isOnline: viewModel.isOnline,
            countdownText: viewModel.countdownDisplayText,
            reminderMinutes: viewModel.reminderMinutes
        )
    }

    private func refreshQuickActionsVisibleCell() {
        guard !viewModel.isMinimalTopRole,
              let sectionIndex = currentSections.firstIndex(of: .quickActions) else {
            return
        }
        let indexPath = IndexPath(item: 0, section: sectionIndex)
        guard let cell = collectionView.cellForItem(at: indexPath) as? HomeQuickActionsCell else {
            return
        }
        cell.apply(isOnline: viewModel.isOnline)
    }

    private func presentDialogsIfNeeded() {
        if viewModel.showPermissionDialog {
            presentDialog(.permission)
            return
        }
        if viewModel.showScenicDialog {
            presentDialog(.scenic)
            return
        }
        if viewModel.showOnlineStatusDialog {
            presentDialog(.onlineStatus)
            return
        }
        if viewModel.showLocationTimeoutDialog {
            presentDialog(.locationTimeout)
            return
        }
        if viewModel.showLocationReportSuccessDialog {
            presentDialog(.locationSuccess)
            return
        }
        activeDialog = nil
    }

    private enum HomeDialogKind {
        case permission
        case scenic
        case onlineStatus
        case locationTimeout
        case locationSuccess
    }

    private func presentDialog(_ kind: HomeDialogKind) {
        guard activeDialog != kind, presentedViewController == nil else { return }
        activeDialog = kind
        switch kind {
        case .permission: presentPermissionDialog()
        case .scenic: presentScenicDialog()
        case .onlineStatus: presentOnlineStatusDialog()
        case .locationTimeout: presentLocationTimeoutDialog()
        case .locationSuccess: presentLocationSuccessDialog()
        }
    }

    private func presentPermissionDialog() {
        let dialog = PermissionRequiredDialogViewController(
            onExitApp: {
                UIApplication.shared.perform(#selector(NSXPCConnection.suspend))
            },
            onGoToApply: { [weak self] in
                guard let self else { return }
                self.activeDialog = nil
                Task { await self.openPermissionApplyFlow() }
            }
        )
        present(dialog, animated: true)
    }

    private func openPermissionApplyFlow() async {
        showLoading()
        let destination = await viewModel.resolvePermissionApplyDestination(api: homeAPI)
        hideLoading()
        viewModel.markNeedsPermissionReload()
        navigationController?.setNavigationBarHidden(false, animated: true)
        switch destination {
        case .apply:
            navigationController?.pushViewController(PermissionApplyViewController(), animated: true)
        case .status(let applyCode):
            navigationController?.pushViewController(
                PermissionApplyStatusViewController(applyCode: applyCode),
                animated: true
            )
        }
    }

    private func presentScenicDialog() {
        let alert = UIAlertController(
            title: "提示",
            message: "您还没有选定入驻景区，请先入驻",
            preferredStyle: .alert
        )
        alert.isModalInPresentation = true
        alert.addAction(UIAlertAction(title: "关闭app", style: .default) { _ in
            UIApplication.shared.perform(#selector(NSXPCConnection.suspend))
        })
        alert.addAction(UIAlertAction(title: "去入驻", style: .default) { [weak self] _ in
            self?.activeDialog = nil
            self?.pushScenicSelection()
        })
        present(alert, animated: true)
    }

    private func presentOnlineStatusDialog() {
        viewModel.hideOnlineStatusSwitchDialog()
        LocationReportDialogPresenter.presentOnlineStatusSwitch(
            from: self,
            isOnline: viewModel.isOnline,
            onConfirm: { [weak self] in
                guard let self else { return }
                self.activeDialog = nil
                Task { await self.viewModel.switchOnlineStatus(api: self.homeAPI) }
            },
            onCancel: { [weak self] in
                self?.viewModel.hideOnlineStatusSwitchDialog()
                self?.activeDialog = nil
            }
        )
    }

    private func presentLocationTimeoutDialog() {
        viewModel.hideLocationTimeoutDialog()
        LocationReportDialogPresenter.presentLocationTimeout(
            from: self,
            onReport: { [weak self] in
                guard let self else { return }
                self.activeDialog = nil
                Task { await self.viewModel.confirmLocationTimeoutReport(api: self.homeAPI) }
            },
            onLater: { [weak self] in
                self?.viewModel.hideLocationTimeoutDialog()
                self?.activeDialog = nil
            }
        )
    }

    private func presentLocationSuccessDialog() {
        let reportTime = viewModel.reportTimeText
        let countdown = viewModel.countdownDisplayText
        viewModel.hideLocationReportSuccessDialog()
        LocationReportDialogPresenter.presentReportSuccess(
            from: self,
            reportTime: reportTime,
            countdown: countdown,
            onDismiss: { [weak self] in
                self?.activeDialog = nil
            }
        )
    }

    private func presentReminderPicker() {
        LocationReportDialogPresenter.presentReminderPicker(from: self) { [weak self] minutes in
            self?.viewModel.updateReminderMinutes(minutes)
        }
    }

    private func presentScenicSelection() {
        pushScenicSelection()
    }

    private func openMessageCenter() {
        navigationController?.pushViewController(MessageCenterViewController(), animated: true)
    }

    private func pushScenicSelection() {
        navigationController?.setNavigationBarHidden(false, animated: true)
        let controller = ScenicSelectionViewController()
        controller.onSelectionComplete = { [weak self] in
            guard let self else { return }
            Task {
                self.viewModel.refreshLocalDisplayState()
                self.viewModel.rebuildCommonMenus()
                await self.viewModel.loadStoreListIfNeeded(api: self.homeAPI)
                self.applyViewModel()
                await self.viewModel.evaluateDialogs(api: self.homeAPI)
                await MainActor.run { self.presentDialogsIfNeeded() }
            }
        }
        navigationController?.pushViewController(controller, animated: true)
    }

    private func startCountdownTimer() {
        countdownTimer?.invalidate()
        countdownTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in
            guard let self else { return }
            self.viewModel.triggerLocationTimeoutIfNeeded()
            self.refreshWorkStatusVisibleCell()
        }
    }

    @objc private func handleAccountDidSwitch() {
        viewModel.markNeedsPermissionReload()
        Task {
            await viewModel.reloadIfNeeded(api: homeAPI)
            await refreshUnreadMessageStatus()
            applyViewModel()
            await evaluateDialogsWithDelay()
        }
    }

    @objc private func handleScenicDidChange() {
        viewModel.refreshLocalDisplayState()
        Task {
            await viewModel.loadStoreListIfNeeded(api: homeAPI)
            applyViewModel()
        }
    }

    @objc private func handleUnreadMessageCountDidChange(_ notification: Notification) {
        guard hasInitialized else { return }
        if let count = notification.userInfo?[NotificationUserInfoKey.unreadCount] as? Int {
            viewModel.updateUnreadMessageCount(count)
            Task { await PushNotificationManager.shared.updateApplicationIconBadgeCount(count) }
        } else {
            Task { await refreshUnreadMessageStatus() }
        }
    }

    private func refreshUnreadMessageStatus() async {
        guard await viewModel.refreshUnreadMessageStatus(api: messageCenterAPI) else { return }
        await PushNotificationManager.shared.updateApplicationIconBadgeCount(viewModel.unreadMessageCount)
    }
}

extension HomeViewController: UICollectionViewDelegate {
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        guard indexPath.section < currentSections.count,
              currentSections[indexPath.section] == .commonApps,
              let item = dataSource.itemIdentifier(for: indexPath),
              case let .menu(menu) = item else {
            return
        }

        HomeRouteHandler.open(
            uri: menu.uri,
            from: self,
            storeItem: viewModel.storeItem,
            onCommonMenusChanged: { [weak self] in
                guard let self else { return }
                self.viewModel.rebuildCommonMenus()
                self.applyViewModel()
            }
        )
    }
}
