import ActivityKit
import Foundation

extension Notification.Name {
    static let liveActivityDismissed = Notification.Name("liveActivityDismissed")
}

extension Activity: @unchecked @retroactive Sendable where Attributes: Sendable {}

@MainActor
final class LiveActivityManager {
    static let shared = LiveActivityManager()
    nonisolated private static let logTag = "LiveActivity"
    private init() {}

    nonisolated(unsafe) private var currentActivity: Activity<SessionActivityAttributes>?
    private var healthCheckTask: Task<Void, Never>?

    /// Check if there's an active session and restart Live Activity if needed
    func restartLiveActivityIfNeeded(activeSession: ClimbSession?, gymName: String?) async {
        // If we have an active session but no Live Activity, restart it
        guard let activeSession = activeSession,
            let gymName = gymName,
            activeSession.status == .active
        else {
            return
        }

        // Check if we have a tracked Live Activity that's still actually running
        if let currentActivity = currentActivity {
            let activities = Activity<SessionActivityAttributes>.activities
            let isStillActive = activities.contains { $0.id == currentActivity.id }

            if isStillActive {
                AppLogger.debug("Live Activity still running: \(currentActivity.id)", tag: Self.logTag)
                return
            } else {
                AppLogger.warning(
                    "Tracked Live Activity \(currentActivity.id) was dismissed, clearing reference",
                    tag: Self.logTag
                )
                self.currentActivity = nil
            }
        }

        // Check if there are ANY active Live Activities for this session
        let existingActivities = Activity<SessionActivityAttributes>.activities
        if let existingActivity = existingActivities.first {
            AppLogger.info("Found existing Live Activity: \(existingActivity.id), using it", tag: Self.logTag)
            self.currentActivity = existingActivity
            return
        }

        AppLogger.info("No Live Activity found, restarting for existing session", tag: Self.logTag)
        await startLiveActivity(for: activeSession, gymName: gymName)
    }

    /// Call this when a ClimbSession starts to begin a Live Activity
    func startLiveActivity(for session: ClimbSession, gymName: String) async {
        AppLogger.info("Starting Live Activity for gym: \(gymName)", tag: Self.logTag)

        await endLiveActivity()

        // Start health checks once we have an active session
        startHealthChecks()

        // Calculate elapsed time if session already started
        let startTime = session.startTime ?? session.date
        let elapsed = Date().timeIntervalSince(startTime)

        let attributes = SessionActivityAttributes(
            gymName: gymName, startTime: startTime)
        let initialContentState = SessionActivityAttributes.ContentState(
            elapsed: elapsed,
            totalAttempts: 0,
            completedProblems: 0
        )

        do {
            let activity = try Activity<SessionActivityAttributes>.request(
                attributes: attributes,
                content: .init(state: initialContentState, staleDate: nil),
                pushType: nil
            )
            self.currentActivity = activity
            AppLogger.info("Live Activity started successfully: \(activity.id)", tag: Self.logTag)
        } catch {
            AppLogger.error(
                """
                Failed to start live activity: \(error)
                Details: \(error.localizedDescription)
                """,
                tag: Self.logTag
            )

            // Check specific error types
            if error.localizedDescription.contains("authorization") {
                AppLogger.warning(
                    "Authorization error - check Live Activity permissions in Settings",
                    tag: Self.logTag
                )
            } else if error.localizedDescription.contains("content") {
                AppLogger.warning("Content error - check ActivityAttributes structure", tag: Self.logTag)
            } else if error.localizedDescription.contains("frequencyLimited") {
                AppLogger.warning("Frequency limited - too many Live Activities started recently", tag: Self.logTag)
            }
        }
    }

    func updateLiveActivity(elapsed: TimeInterval, totalAttempts: Int, completedProblems: Int) async
    {
        guard let activity = currentActivity else {
            AppLogger.warning("No current activity to update", tag: Self.logTag)
            return
        }

        let activities = Activity<SessionActivityAttributes>.activities
        let isStillActive = activities.contains { $0.id == activity.id }

        if !isStillActive {
            AppLogger.warning(
                "Tracked Live Activity \(activity.id) is no longer active, clearing reference",
                tag: Self.logTag
            )
            self.currentActivity = nil
            return
        }

        AppLogger.debug(
            "Updating Live Activity - Attempts: \(totalAttempts), Completed: \(completedProblems)",
            tag: Self.logTag
        )

        let updatedContentState = SessionActivityAttributes.ContentState(
            elapsed: elapsed,
            totalAttempts: totalAttempts,
            completedProblems: completedProblems
        )

        await activity.update(.init(state: updatedContentState, staleDate: nil))
    }

    func endLiveActivity() async {
        stopHealthChecks()

        if let activity = currentActivity {
            AppLogger.info("Ending tracked Live Activity: \(activity.id)", tag: Self.logTag)
            await activity.end(nil, dismissalPolicy: .immediate)
            self.currentActivity = nil
            AppLogger.info("Tracked Live Activity ended successfully", tag: Self.logTag)
        }

        AppLogger.debug("Checking for any remaining active activities...", tag: Self.logTag)
        let activities = Activity<SessionActivityAttributes>.activities

        if activities.isEmpty {
            AppLogger.debug("No additional activities found", tag: Self.logTag)
        } else {
            AppLogger.info("Found \(activities.count) additional active activities, ending them...", tag: Self.logTag)
            for activity in activities {
                AppLogger.debug("Force ending activity: \(activity.id)", tag: Self.logTag)
                await activity.end(nil, dismissalPolicy: .immediate)
            }
            AppLogger.info("All Live Activities ended successfully", tag: Self.logTag)
        }
    }

    /// Check if Live Activities are available and authorized
    func checkLiveActivityAvailability() -> String {
        let authorizationInfo = ActivityAuthorizationInfo()
        let status = authorizationInfo.areActivitiesEnabled
        let allActivities = Activity<SessionActivityAttributes>.activities

        let message = """
            Live Activity Status:
            • Enabled: \(status)
            • Authorization: \(authorizationInfo.areActivitiesEnabled ? "Granted" : "Denied/Unknown")
            • Tracked Activity: \(currentActivity?.id.description ?? "None")
            • All Active Activities: \(allActivities.count)
            """

        AppLogger.info(message, tag: Self.logTag)
        return message
    }

    /// Force check and cleanup dismissed Live Activities
    func cleanupDismissedActivities() async {
        let activities = Activity<SessionActivityAttributes>.activities

        if let currentActivity = currentActivity {
            let isStillActive = activities.contains { $0.id == currentActivity.id }
            if !isStillActive {
                AppLogger.info("Cleaning up dismissed Live Activity: \(currentActivity.id)", tag: Self.logTag)
                self.currentActivity = nil
            }
        }
    }

    func startHealthChecks() {
        stopHealthChecks()

        AppLogger.debug("Starting Live Activity health checks", tag: Self.logTag)
        healthCheckTask = Task {
            while !Task.isCancelled {
                try? await Task.sleep(for: .seconds(30))
                guard !Task.isCancelled else { break }
                await performHealthCheck()
            }
        }
    }

    func stopHealthChecks() {
        healthCheckTask?.cancel()
        healthCheckTask = nil
        AppLogger.debug("Stopped Live Activity health checks", tag: Self.logTag)
    }

    private func performHealthCheck() async {
        guard let currentActivity = currentActivity else { return }

        AppLogger.debug("Performing Live Activity health check", tag: Self.logTag)

        let activities = Activity<SessionActivityAttributes>.activities
        let isStillActive = activities.contains { $0.id == currentActivity.id }

        if !isStillActive {
            AppLogger.warning("Health check failed - Live Activity was dismissed", tag: Self.logTag)
            self.currentActivity = nil
            NotificationCenter.default.post(name: .liveActivityDismissed, object: nil)
        } else {
            AppLogger.debug("Live Activity health check passed", tag: Self.logTag)
        }
    }

    func getCurrentActivityStatus() -> String {
        let activities = Activity<SessionActivityAttributes>.activities
        let trackedStatus = currentActivity != nil ? "Tracked" : "None"
        let actualCount = activities.count

        return "Status: \(trackedStatus) | Active Count: \(actualCount)"
    }

    func startPeriodicUpdates(for session: ClimbSession, totalAttempts: Int, completedProblems: Int)
    {
        guard currentActivity != nil else { return }

        Task {
            while currentActivity != nil {
                let elapsed = Date().timeIntervalSince(session.startTime ?? session.date)
                await updateLiveActivity(
                    elapsed: elapsed,
                    totalAttempts: totalAttempts,
                    completedProblems: completedProblems
                )
                try? await Task.sleep(for: .seconds(30))
            }
        }
    }
}
