import Foundation
import Observation
import UIKit

@MainActor
@Observable
final class VideoMonitor {
    enum DefaultsKey {
        static let deviceToken = "deviceToken"
        static let registeredDeviceToken = "registeredDeviceToken"
    }

    enum Status: Equatable {
        case idle
        case checking
        case ready
        case newVideo
        case empty
        case failed(String)

        var title: String {
            switch self {
            case .idle:
                "未开始"
            case .checking:
                "检查中"
            case .ready:
                "监测中"
            case .newVideo:
                "发现更新"
            case .empty:
                "暂无数据"
            case .failed:
                "检查失败"
            }
        }
    }

    var latestVideo: VideoRecord?
    var lastCheckedAt: Date?
    var status: Status = .idle
    var notificationsEnabled = false
    var deviceToken: String = UserDefaults.standard.string(forKey: DefaultsKey.deviceToken) ?? ""
    var remoteNotificationStatus = "等待注册 APNs"

    private var monitoringTask: Task<Void, Never>?
    private let checkInterval: Duration = .seconds(10)
    private let service = VideoCheckService()

    var csvURLText: String {
        get {
            service.csvURLText
        }
        set {
            service.updateCSVURL(newValue)
        }
    }

    var emptyStateMessage: String {
        switch status {
        case .failed(let message):
            "无法读取服务器 CSV：\(message)\n当前地址：\(csvURLText)"
        case .empty:
            "服务器 CSV 中还没有可解析的视频信息\n当前地址：\(csvURLText)"
        default:
            "正在读取服务器 CSV\n当前地址：\(csvURLText)"
        }
    }

    func start() {
        guard monitoringTask == nil else {
            return
        }

        monitoringTask = Task {
            notificationsEnabled = await NotificationService().requestAuthorization()
            if notificationsEnabled {
                UIApplication.shared.registerForRemoteNotifications()
                remoteNotificationStatus = "正在注册 APNs"
            } else {
                remoteNotificationStatus = "通知权限未开启"
            }
            await checkNow()

            while !Task.isCancelled {
                do {
                    try await Task.sleep(for: checkInterval)
                    await checkNow()
                } catch {
                    return
                }
            }
        }
    }

    func stop() {
        monitoringTask?.cancel()
        monitoringTask = nil
    }

    func checkNow() async {
        status = .checking

        do {
            let result = try await service.checkForCSVChange(sendNotification: false)
            lastCheckedAt = Date()

            switch result {
            case .initialized(let snapshot), .unchanged(let snapshot):
                latestVideo = snapshot.latestVideo
                status = snapshot.latestVideo == nil ? .empty : .ready
            case .csvChanged(let snapshot):
                latestVideo = snapshot.latestVideo
                status = .newVideo
            }
        } catch {
            status = .failed(error.localizedDescription)
        }
    }

    func updateCSVURL(_ urlText: String) async {
        csvURLText = urlText
        await checkNow()
    }

    func resetCSVURLToDefault() async {
        service.resetCSVURLToDefault()
        await checkNow()
    }

    func handleDeviceToken(_ token: String) {
        deviceToken = token
        remoteNotificationStatus = "正在上传 token 到服务器"
        Task {
            await registerDeviceTokenIfNeeded(token)
        }
    }

    private func registerDeviceTokenIfNeeded(_ token: String) async {
        let defaults = UserDefaults.standard
        if defaults.string(forKey: DefaultsKey.registeredDeviceToken) == token {
            remoteNotificationStatus = "token 已注册到服务器"
            return
        }

        do {
            try await DeviceTokenRegistrationService().register(token: token)
            defaults.set(token, forKey: DefaultsKey.registeredDeviceToken)
            remoteNotificationStatus = "token 已注册到服务器"
        } catch {
            remoteNotificationStatus = "token 上传失败：\(error.localizedDescription)"
        }
    }
}

struct DeviceTokenRegistrationService {
    enum RegistrationError: LocalizedError {
        case invalidURL
        case requestFailed(Int)

        var errorDescription: String? {
            switch self {
            case .invalidURL:
                "Token 注册地址无效"
            case .requestFailed(let statusCode):
                "Token 注册失败：HTTP \(statusCode)"
            }
        }
    }

    func register(token: String) async throws {
        guard let url = URL(string: CSVVideoStore.tokenRegistrationURL) else {
            throw RegistrationError.invalidURL
        }

        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.timeoutInterval = 20
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.httpBody = try JSONSerialization.data(withJSONObject: ["token": token])

        let (_, response) = try await URLSession.shared.data(for: request)
        guard let httpResponse = response as? HTTPURLResponse else {
            return
        }
        guard (200..<300).contains(httpResponse.statusCode) else {
            throw RegistrationError.requestFailed(httpResponse.statusCode)
        }
    }
}
