//
//  StatisticsViewModel.swift
//  suixinkan
//

import Foundation

/// 数据 Tab ViewModel，对齐 Android `StatisticsViewModel`。
final class StatisticsViewModel {

    private(set) var statistics: StatisticsSummaryResponse?
    private(set) var selectedPeriod = StatisticsSummaryPeriod.today.rawValue
    private(set) var selectedPeriodTime = ""
    private(set) var statisticsList: [StatisticsDailyItem] = []
    private(set) var statisticsRangeText = "今日"
    private(set) var rangeStartDate: Date?
    private(set) var rangeEndDate: Date?
    private(set) var isRefreshing = false
    private(set) var canLoadMore = false
    private(set) var isLoadingSummary = false

    var onStateChange: (() -> Void)?
    var onShowMessage: ((String) -> Void)?

    private var currentPage = 1
    private var totalCount = 0
    private var startTime = ""
    private var endTime = ""

    private let apiDateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.calendar = Calendar(identifier: .gregorian)
        formatter.locale = Locale(identifier: "zh_CN")
        formatter.dateFormat = "yyyy-MM-dd"
        return formatter
    }()

    private let uiDateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.calendar = Calendar(identifier: .gregorian)
        formatter.locale = Locale(identifier: "zh_CN")
        formatter.dateFormat = "yyyy/MM/dd"
        return formatter
    }()

    private var rangeMap: [String: String] {
        Dictionary(uniqueKeysWithValues: StatisticsSummaryPeriod.allCases.map { ($0.rawValue, $0.rangeValue) })
    }

    /// 首次进入 Tab 时初始化汇总与列表。
    func initStatistics(api: StatisticsAPI) async {
        await loadStatistics(StatisticsSummaryPeriod.today.rawValue, api: api)
        await updateRangeByShortcut(StatisticsRangeShortcut.today.rawValue, api: api)
    }

    /// 切换汇总周期 chip。
    func selectPeriod(_ period: String, api: StatisticsAPI) async {
        await loadStatistics(period, api: api)
    }

    /// 加载汇总数据。
    func loadStatistics(_ period: String, api: StatisticsAPI) async {
        isLoadingSummary = true
        selectedPeriod = period
        updateSelectedPeriodTime(period)
        notifyStateChange()

        let range = rangeMap[period] ?? StatisticsSummaryPeriod.today.rangeValue
        defer {
            isLoadingSummary = false
            notifyStateChange()
        }

        do {
            statistics = try await fetchSummary(range: range, api: api)
        } catch is CancellationError {
            return
        } catch {
            onShowMessage?(error.localizedDescription)
        }
    }

    /// 下拉刷新列表。
    func refreshStatistics(api: StatisticsAPI) async {
        currentPage = 1
        isRefreshing = true
        notifyStateChange()
        await fetchStatisticsList(api: api)
    }

    /// 滚动加载更多。
    func loadMore(api: StatisticsAPI) async {
        guard statisticsList.count < totalCount else { return }
        currentPage += 1
        await fetchStatisticsList(api: api)
    }

    /// 列表区快捷日期。
    func updateRangeByShortcut(_ shortcut: String, api: StatisticsAPI) async {
        let today = startOfToday()

        switch shortcut {
        case StatisticsRangeShortcut.today.rawValue:
            await applyRange(start: today, end: today, label: StatisticsRangeShortcut.today.rawValue, api: api)
        case StatisticsRangeShortcut.yesterday.rawValue:
            let yesterday = calendar.date(byAdding: .day, value: -1, to: today) ?? today
            await applyRange(start: yesterday, end: yesterday, label: StatisticsRangeShortcut.yesterday.rawValue, api: api)
        case StatisticsRangeShortcut.last7Days.rawValue:
            let start = calendar.date(byAdding: .day, value: -6, to: today) ?? today
            await applyRange(start: start, end: today, label: StatisticsRangeShortcut.last7Days.rawValue, api: api)
        case StatisticsRangeShortcut.all.rawValue:
            startTime = ""
            endTime = ""
            rangeStartDate = nil
            rangeEndDate = nil
            statisticsRangeText = StatisticsRangeShortcut.all.rawValue
            notifyStateChange()
            await refreshStatistics(api: api)
        default:
            break
        }
    }

    /// 日历自定义区间。
    func updateRangeByCalendar(start: Date, end: Date, api: StatisticsAPI) async {
        await applyRange(start: startOfDay(start), end: startOfDay(end), label: nil, api: api)
    }

    /// 供测试读取当前列表查询起始时间。
    var listStartTime: String { startTime }

    /// 供测试读取当前列表查询结束时间。
    var listEndTime: String { endTime }

    /// 供测试读取当前页码。
    var listCurrentPage: Int { currentPage }

    /// 供测试读取 range 映射。
    func rangeValue(for period: String) -> String {
        rangeMap[period] ?? StatisticsSummaryPeriod.today.rangeValue
    }

    /// 供测试生成周期日期文案。
    func formattedSelectedPeriodTime(for period: String) -> String {
        let previous = selectedPeriod
        selectedPeriod = period
        updateSelectedPeriodTime(period)
        let result = selectedPeriodTime
        selectedPeriod = previous
        return result
    }

    private func fetchStatisticsList(api: StatisticsAPI) async {
        defer {
            isRefreshing = false
            notifyStateChange()
        }

        do {
            let response = try await fetchDailyList(api: api)
            totalCount = response.total
            if currentPage == 1 {
                statisticsList = response.data
            } else {
                statisticsList.append(contentsOf: response.data)
            }
            canLoadMore = statisticsList.count < totalCount
            notifyStateChange()
        } catch is CancellationError {
            return
        } catch {
            onShowMessage?(error.localizedDescription)
        }
    }

    private func applyRange(start: Date, end: Date, label: String?, api: StatisticsAPI) async {
        startTime = apiDateFormatter.string(from: start)
        endTime = apiDateFormatter.string(from: end)
        rangeStartDate = start
        rangeEndDate = end
        statisticsRangeText = label ?? "\(uiDateFormatter.string(from: start))至\(uiDateFormatter.string(from: end))"
        notifyStateChange()
        await refreshStatistics(api: api)
    }

    private func updateSelectedPeriodTime(_ period: String) {
        let today = startOfToday()
        let (start, end): (Date, Date)

        switch period {
        case StatisticsSummaryPeriod.today.rawValue:
            (start, end) = (today, today)
        case StatisticsSummaryPeriod.yesterday.rawValue:
            let yesterday = calendar.date(byAdding: .day, value: -1, to: today) ?? today
            (start, end) = (yesterday, yesterday)
        case StatisticsSummaryPeriod.last7Days.rawValue:
            let startDate = calendar.date(byAdding: .day, value: -6, to: today) ?? today
            (start, end) = (startDate, today)
        case StatisticsSummaryPeriod.thisMonth.rawValue:
            let components = calendar.dateComponents([.year, .month], from: today)
            let firstDay = calendar.date(from: components) ?? today
            (start, end) = (firstDay, today)
        default:
            (start, end) = (today, today)
        }

        selectedPeriodTime = "\(uiDateFormatter.string(from: start))至\(uiDateFormatter.string(from: end))"
    }

    private func fetchSummary(range: String, api: StatisticsAPI) async throws -> StatisticsSummaryResponse {
        switch currentRole {
        case .storeAdmin:
            let storeId = try resolveStoreId()
            return try await api.storeSummary(storeId: storeId, range: range)
        case .scenicAdmin:
            return try await api.scenicAdminSummary(scenicId: AppStore.shared.session.currentScenicId, range: range)
        default:
            return try await api.photographerSummary(scenicId: AppStore.shared.session.currentScenicId, range: range)
        }
    }

    private func fetchDailyList(api: StatisticsAPI) async throws -> ListDataResponse<StatisticsDailyItem> {
        switch currentRole {
        case .storeAdmin:
            let storeId = try resolveStoreId()
            return try await api.storeDaily(
                storeId: storeId,
                startTime: startTime,
                endTime: endTime,
                page: currentPage
            )
        case .scenicAdmin:
            return try await api.scenicAdminDaily(
                scenicId: AppStore.shared.session.currentScenicId,
                startTime: startTime,
                endTime: endTime,
                page: currentPage
            )
        default:
            return try await api.photographerDaily(
                scenicId: AppStore.shared.session.currentScenicId,
                startTime: startTime,
                endTime: endTime,
                page: currentPage
            )
        }
    }

    private var currentRole: AppRoleCode {
        AppStore.shared.session.currentAppRole ?? .photographer
    }

    private func resolveStoreId() throws -> Int {
        let storeId = AppStore.shared.session.currentStoreId
        guard storeId > 0 else {
            throw StatisticsFlowError.missingStore
        }
        return storeId
    }

    private var calendar: Calendar {
        var calendar = Calendar(identifier: .gregorian)
        calendar.locale = Locale(identifier: "zh_CN")
        calendar.timeZone = TimeZone.current
        return calendar
    }

    private func startOfToday() -> Date {
        startOfDay(Date())
    }

    private func startOfDay(_ date: Date) -> Date {
        calendar.startOfDay(for: date)
    }

    private func notifyStateChange() {
        onStateChange?()
    }
}

enum StatisticsFlowError: LocalizedError {
    case missingStore

    var errorDescription: String? {
        switch self {
        case .missingStore: "请先选择店铺"
        }
    }
}
