import Foundation
import UserNotifications

enum VideoCheckResult {
    case initialized(CSVSnapshot)
    case csvChanged(CSVSnapshot)
    case unchanged(CSVSnapshot)
}

struct VideoCheckService {
    private enum DefaultsKey {
        static let lastSeenPublishTime = "lastSeenPublishTime"
        static let lastSeenCSVHash = "lastSeenCSVHash"
        static let csvURL = "csvURL"
    }

    private let store: CSVVideoStore
    private let defaults: UserDefaults
    private let notificationCenter: UNUserNotificationCenter

    init(
        store: CSVVideoStore = CSVVideoStore(),
        defaults: UserDefaults = .standard,
        notificationCenter: UNUserNotificationCenter = .current()
    ) {
        self.store = store
        self.defaults = defaults
        self.notificationCenter = notificationCenter
    }

    var csvURLText: String {
        get {
            guard let storedURL = defaults.string(forKey: DefaultsKey.csvURL), !storedURL.isEmpty else {
                return CSVVideoStore.defaultCSVURL
            }

            let normalizedURL = normalizedCSVURL(storedURL)
            if normalizedURL != storedURL {
                defaults.set(normalizedURL, forKey: DefaultsKey.csvURL)
            }

            return normalizedURL
        }
        set {
            defaults.set(normalizedCSVURL(newValue), forKey: DefaultsKey.csvURL)
        }
    }

    func updateCSVURL(_ urlText: String) {
        defaults.set(normalizedCSVURL(urlText), forKey: DefaultsKey.csvURL)
    }

    func resetCSVURLToDefault() {
        defaults.set(CSVVideoStore.defaultCSVURL, forKey: DefaultsKey.csvURL)
    }

    func loadCurrentVideo() async throws -> VideoRecord? {
        try await store.loadLatestVideo(from: csvURLText)
    }

    func checkForCSVChange(sendNotification: Bool = true) async throws -> VideoCheckResult {
        let snapshot = try await store.loadSnapshot(from: csvURLText)

        guard let lastSeenHash = defaults.string(forKey: DefaultsKey.lastSeenCSVHash) else {
            saveBaseline(snapshot)
            return .initialized(snapshot)
        }

        if snapshot.contentHash != lastSeenHash {
            saveBaseline(snapshot)
            if sendNotification {
                await NotificationService(center: notificationCenter).sendCSVChangedNotification(snapshot)
            }
            return .csvChanged(snapshot)
        }

        return .unchanged(snapshot)
    }

    private func saveBaseline(_ snapshot: CSVSnapshot) {
        defaults.set(snapshot.contentHash, forKey: DefaultsKey.lastSeenCSVHash)
        if let video = snapshot.latestVideo {
            defaults.set(video.publishDate.timeIntervalSince1970, forKey: DefaultsKey.lastSeenPublishTime)
        }
    }

    private func normalizedCSVURL(_ urlText: String) -> String {
        var normalizedURL = urlText.trimmingCharacters(in: .whitespacesAndNewlines)
        normalizedURL = normalizedURL.replacingOccurrences(
            of: "bilibili_latests_videos_",
            with: "bilibili_latest_videos_"
        )

        if normalizedURL == CSVVideoStore.legacySimulatorCSVURL
            || normalizedURL == CSVVideoStore.legacyLocalCSVURL {
            normalizedURL = CSVVideoStore.defaultCSVURL
        }

        return normalizedURL
    }
}

struct NotificationService {
    private let center: UNUserNotificationCenter

    init(center: UNUserNotificationCenter = .current()) {
        self.center = center
    }

    func requestAuthorization() async -> Bool {
        do {
            return try await center.requestAuthorization(options: [.alert, .badge, .sound])
        } catch {
            return false
        }
    }

    func sendNewVideoNotification(_ video: VideoRecord) async {
        let content = UNMutableNotificationContent()
        content.title = video.title
        content.body = "发布时间：\(video.publishTimeText)"
        content.sound = .default
        content.userInfo = ["videoURL": video.videoURL.absoluteString]

        let request = UNNotificationRequest(
            identifier: "new-video-\(video.id)",
            content: content,
            trigger: nil
        )

        try? await center.add(request)
    }

    func sendCSVChangedNotification(_ snapshot: CSVSnapshot) async {
        let content = UNMutableNotificationContent()
        if let video = snapshot.latestVideo {
            content.title = video.title
            content.body = "发布时间：\(video.publishTimeText)"
            content.userInfo = ["videoURL": video.videoURL.absoluteString]
        } else {
            return
        }
        content.sound = .default

        let request = UNNotificationRequest(
            identifier: "csv-changed-\(snapshot.contentHash)",
            content: content,
            trigger: nil
        )

        try? await center.add(request)
    }
}
