//
//  ScheduleViewModels.swift
//  suixinkan
//
//  Created by Codex on 2026/6/24.
//

import Foundation
import Combine

/// 排班错误实体，表示表单校验失败。
enum ScheduleValidationError: LocalizedError, Equatable {
    case missingScenic
    case missingName
    case invalidTime

    /// 错误文案。
    var errorDescription: String? {
        switch self {
        case .missingScenic: return "当前缺少景区信息"
        case .missingName: return "请输入日程名称"
        case .invalidTime: return "结束时间必须晚于开始时间"
        }
    }
}

/// 排班管理 ViewModel，负责月份日期标记、某日列表和删除操作。
@MainActor
final class ScheduleManagementViewModel: ObservableObject {
    @Published var monthDate = Date.scheduleFirstDayOfMonth
    @Published var selectedDate = Date()
    @Published private(set) var markedDays: Set<String> = []
    @Published private(set) var items: [ScheduleItem] = []
    @Published private(set) var loading = false
    @Published var errorMessage: String?

    /// 切换到上一个月并重新加载。
    func previousMonth(api: any ScheduleServing, scenicId: Int?) async {
        monthDate = Calendar.current.date(byAdding: .month, value: -1, to: monthDate) ?? monthDate
        selectedDate = monthDate
        await reload(api: api, scenicId: scenicId)
    }

    /// 切换到下一个月并重新加载。
    func nextMonth(api: any ScheduleServing, scenicId: Int?) async {
        monthDate = Calendar.current.date(byAdding: .month, value: 1, to: monthDate) ?? monthDate
        selectedDate = monthDate
        await reload(api: api, scenicId: scenicId)
    }

    /// 选择日期并加载当天排班。
    func selectDate(_ date: Date, api: any ScheduleServing, scenicId: Int?) async {
        selectedDate = date
        await loadDay(api: api, scenicId: scenicId)
    }

    /// 重新加载月份日期标记和当前日排班。
    func reload(api: any ScheduleServing, scenicId: Int?) async {
        guard let scenicId else {
            reset()
            return
        }
        loading = true
        errorMessage = nil
        defer { loading = false }
        do {
            async let days = api.monthScheduleDays(scenicId: scenicId, yearMonth: monthDate.scheduleYearMonthText)
            async let list = api.dayScheduleList(scenicId: scenicId, date: selectedDate.scheduleDayText)
            markedDays = Set(try await days)
            items = try await list
        } catch {
            items = []
            markedDays = []
            errorMessage = error.localizedDescription
        }
    }

    /// 只加载当前选中日期的排班列表。
    func loadDay(api: any ScheduleServing, scenicId: Int?) async {
        guard let scenicId else {
            reset()
            return
        }
        do {
            items = try await api.dayScheduleList(scenicId: scenicId, date: selectedDate.scheduleDayText)
        } catch {
            items = []
            errorMessage = error.localizedDescription
        }
    }

    /// 删除排班后刷新当前日和月份标记。
    func delete(item: ScheduleItem, api: any ScheduleServing, scenicId: Int?) async {
        do {
            try await api.deleteSchedule(id: item.id)
            await reload(api: api, scenicId: scenicId)
        } catch {
            errorMessage = error.localizedDescription
        }
    }

    private func reset() {
        markedDays = []
        items = []
        loading = false
    }
}

/// 新增排班 ViewModel，负责可选订单加载、表单校验和提交。
@MainActor
final class ScheduleAddViewModel: ObservableObject {
    @Published var draft = ScheduleDraft()
    @Published private(set) var availableOrders: [AvailableOrderResponse] = []
    @Published private(set) var loadingOrders = false
    @Published private(set) var submitting = false
    @Published var errorMessage: String?

    /// 加载可关联订单列表。
    func loadOrders(api: any ScheduleServing, scenicId: Int?) async {
        guard let scenicId else {
            availableOrders = []
            return
        }
        loadingOrders = true
        defer { loadingOrders = false }
        do {
            availableOrders = try await api.availableOrderList(scenicId: scenicId)
        } catch {
            availableOrders = []
            errorMessage = error.localizedDescription
        }
    }

    /// 提交新增排班请求。
    func submit(api: any ScheduleServing, scenicId: Int?, startDate: Date, endDate: Date) async -> Bool {
        guard !submitting else { return false }
        guard let scenicId else { return fail(.missingScenic) }
        let normalizedName = draft.name.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !normalizedName.isEmpty else { return fail(.missingName) }
        guard endDate > startDate else { return fail(.invalidTime) }

        submitting = true
        errorMessage = nil
        defer { submitting = false }
        do {
            try await api.addSchedule(
                AddScheduleRequest(
                    scenicId: "\(scenicId)",
                    name: normalizedName,
                    remark: draft.remark.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ? normalizedName : draft.remark.trimmingCharacters(in: .whitespacesAndNewlines),
                    startTime: startDate.scheduleHourMinuteText,
                    endTime: endDate.scheduleHourMinuteText,
                    scheduleDate: startDate.scheduleDayText,
                    orderNumber: draft.finalOrderNumber()
                )
            )
            return true
        } catch {
            errorMessage = normalizedError(error)
            return false
        }
    }

    /// 根据旧后端英文错误归一化为中文。
    private func normalizedError(_ error: Error) -> String {
        let text = error.localizedDescription
        if text.contains("start time") || text.contains("format H:i") {
            return "开始时间格式不正确，请重新选择开始时间"
        }
        if text.contains("end time") {
            return "结束时间格式不正确，请重新选择结束时间"
        }
        return text
    }

    private func fail(_ error: ScheduleValidationError) -> Bool {
        errorMessage = error.localizedDescription
        return false
    }
}

extension Date {
    /// 当前月份第一天。
    static var scheduleFirstDayOfMonth: Date {
        Calendar.current.date(from: Calendar.current.dateComponents([.year, .month], from: Date())) ?? Date()
    }

    /// yyyy-MM 格式的月份文本。
    var scheduleYearMonthText: String {
        let formatter = DateFormatter()
        formatter.locale = Locale(identifier: "zh_CN")
        formatter.dateFormat = "yyyy-MM"
        return formatter.string(from: self)
    }

    /// yyyy-MM-dd 格式的日期文本。
    var scheduleDayText: String {
        let formatter = DateFormatter()
        formatter.locale = Locale(identifier: "zh_CN")
        formatter.dateFormat = "yyyy-MM-dd"
        return formatter.string(from: self)
    }

    /// HH:mm 格式的时间文本。
    var scheduleHourMinuteText: String {
        let formatter = DateFormatter()
        formatter.locale = Locale(identifier: "zh_CN")
        formatter.dateFormat = "HH:mm"
        return formatter.string(from: self)
    }

    /// 当前月份的全部日期。
    var scheduleDaysInMonth: [Date] {
        let calendar = Calendar.current
        let range = calendar.range(of: .day, in: .month, for: self) ?? 1..<2
        let components = calendar.dateComponents([.year, .month], from: self)
        return range.compactMap { day in
            var next = components
            next.day = day
            return calendar.date(from: next)
        }
    }
}
