//
//  SubscriptionsView.swift
//  Yattee
//
//  Subscriptions tab with channel filter strip and feed.
//

import SwiftUI

struct SubscriptionsView: View {
    @Environment(\.appEnvironment) private var appEnvironment
    @Namespace private var sheetTransition
    @State private var feedCache = SubscriptionFeedCache.shared
    @State private var subscriptions: [Subscription] = []
    @State private var subscriptionsLoaded = false
    @State private var selectedChannelID: String? = nil
    @State private var errorMessage: String?
    @State private var watchEntriesMap: [String: WatchEntry] = [:]
    @State private var showViewOptions = false

    // View options (persisted)
    @AppStorage("subscriptionsLayout") private var layout: VideoListLayout = .list
    @AppStorage("subscriptionsRowStyle") private var rowStyle: VideoRowStyle = .regular
    @AppStorage("subscriptionsGridColumns") private var gridColumns = 2
    @AppStorage("subscriptionsHideWatched") private var hideWatched = false
    @AppStorage("subscriptionsChannelStripSize") private var channelStripSize: ChannelStripSize = .normal

    /// List style from centralized settings.
    private var listStyle: VideoListStyle {
        appEnvironment?.settingsManager.listStyle ?? .inset
    }

    // Grid layout configuration
    @State private var viewWidth: CGFloat = 0
    private var gridConfig: GridLayoutConfiguration {
        GridLayoutConfiguration(viewWidth: viewWidth, gridColumns: gridColumns)
    }

    private var isShowingFullScreenError: Bool {
        if case .error = feedCache.feedLoadState, feedCache.videos.isEmpty {
            return true
        }
        return false
    }

    private var dataManager: DataManager? { appEnvironment?.dataManager }
    private var subscriptionService: SubscriptionService? { appEnvironment?.subscriptionService }
    private var accentColor: Color { appEnvironment?.settingsManager.accentColor.color ?? .accentColor }
    private var yatteeServer: Instance? {
        appEnvironment?.instancesManager.enabledYatteeServerInstances.first
    }
    private var yatteeServerURL: URL? { yatteeServer?.url }
    private var yatteeServerAuthHeader: String? {
        guard let server = yatteeServer else { return nil }
        return appEnvironment?.yatteeServerCredentialsManager.basicAuthHeader(for: server)
    }

    /// Generates a unique ID based on instances configuration.
    private var instanceConfigurationID: String {
        guard let instances = appEnvironment?.instancesManager.instances else {
            return "none"
        }
        return instances
            .filter { $0.type == .yatteeServer }
            .map { "\($0.id):\($0.isEnabled):\($0.apiKey?.isEmpty == false)" }
            .joined(separator: "|")
    }

    /// Videos filtered by selected channel and watch status.
    private var filteredVideos: [Video] {
        var videos = feedCache.videos

        if let channelID = selectedChannelID {
            videos = videos.filter { $0.author.id == channelID }
        }

        if hideWatched {
            videos = videos.filter { video in
                guard let entry = watchEntriesMap[video.id.videoID] else { return true }
                return !entry.isFinished
            }
        }

        return videos
    }

    /// The currently selected subscription (if any).
    private var selectedSubscription: Subscription? {
        guard let channelID = selectedChannelID else { return nil }
        return subscriptions.first { $0.channelID == channelID }
    }

    /// Banner showing feed loading progress when server is fetching channels.
    @ViewBuilder
    private var feedStatusBanner: some View {
        switch feedCache.feedLoadState {
        case .partiallyLoaded(let ready, let pending, let errors):
            let total = ready + pending + errors
            HStack(spacing: 8) {
                if pending > 0 {
                    ProgressView()
                        .scaleEffect(0.8)
                }
                if errors > 0 {
                    Text("subscriptions.loadingFeedWithErrors \(ready) \(total) \(errors)")
                        .font(.caption)
                        .foregroundStyle(.secondary)
                        .monospacedDigit()
                } else {
                    Text("subscriptions.loadingFeed \(ready) \(total)")
                        .font(.caption)
                        .foregroundStyle(.secondary)
                        .monospacedDigit()
                }
            }
            .padding(.horizontal)
            .padding(.vertical, 8)
            .frame(maxWidth: .infinity)
            #if os(tvOS)
            .background(Color.black.opacity(0.3))
            #endif

        case .error(let error):
            HStack(spacing: 8) {
                Image(systemName: "exclamationmark.triangle")
                Text(errorMessage(for: error))
                    .font(.caption)
            }
            .foregroundStyle(.red)
            .padding(.horizontal)
            .padding(.vertical, 8)
            .frame(maxWidth: .infinity)
            #if os(tvOS)
            .background(Color.black.opacity(0.3))
            #endif

        case .loadingMore:
            HStack(spacing: 8) {
                ProgressView()
                    .scaleEffect(0.8)
                Text(String(localized: "subscriptions.loadingMore"))
                    .font(.caption)
                    .foregroundStyle(.secondary)
            }
            .padding(.horizontal)
            .padding(.vertical, 8)
            .frame(maxWidth: .infinity)
            #if os(tvOS)
            .background(Color.black.opacity(0.3))
            #endif

        default:
            EmptyView()
        }
    }

    /// Converts feed error to localized message.
    private func errorMessage(for error: FeedLoadState.FeedLoadError) -> String {
        switch error {
        case .yatteeServerRequired:
            return String(localized: "subscriptions.error.yatteeServerRequired")
        case .notAuthenticated:
            return String(localized: "subscriptions.error.notAuthenticated")
        case .networkError(let message):
            return message
        }
    }

    /// Subscriptions sorted by most recent video upload date.
    private var sortedSubscriptions: [Subscription] {
        var latestVideoDate: [String: Date] = [:]
        for video in feedCache.videos {
            let channelID = video.author.id
            let videoDate = video.publishedAt ?? .distantPast
            if let existing = latestVideoDate[channelID] {
                if videoDate > existing {
                    latestVideoDate[channelID] = videoDate
                }
            } else {
                latestVideoDate[channelID] = videoDate
            }
        }

        return subscriptions.sorted { sub1, sub2 in
            let date1 = latestVideoDate[sub1.channelID] ?? .distantPast
            let date2 = latestVideoDate[sub2.channelID] ?? .distantPast
            return date1 > date2
        }
    }

    /// Gets the watch progress (0.0-1.0) for a video, or nil if not watched/finished.
    private func watchProgress(for video: Video) -> Double? {
        guard let entry = watchEntriesMap[video.id.videoID] else { return nil }
        let progress = entry.progress
        return progress > 0 && progress < 1 ? progress : nil
    }

    var body: some View {
        GeometryReader { geometry in
            ScrollViewReader { proxy in
                ZStack {
                    Group {
                        switch layout {
                        case .list:
                            listContent
                        case .grid:
                            gridContent
                        }
                    }
                    .refreshable {
                        guard let appEnvironment else { return }
                        LoggingService.shared.info("User initiated pull-to-refresh in Subscriptions view", category: .general)
                        await loadSubscriptionsAsync()
                        await feedCache.refresh(using: appEnvironment)
                        LoggingService.shared.info("Pull-to-refresh completed", category: .general)
                    }
                    .navigationTitle(String(localized: "tabs.subscriptions"))
                    #if !os(tvOS)
                    .toolbarTitleDisplayMode(.inlineLarge)
                    #endif
                    .toolbar {
                        ToolbarItem(placement: .primaryAction) {
                            Button {
                                showViewOptions = true
                            } label: {
                                Label(String(localized: "viewOptions.title"), systemImage: "slider.horizontal.3")
                            }
                            .liquidGlassTransitionSource(id: "subscriptionsViewOptions", in: sheetTransition)
                        }
                    }
                    .sheet(isPresented: $showViewOptions) {
                        NavigationStack {
                            Form {
                                Section {
                                    // Layout picker (segmented)
                                    Picker(selection: $layout) {
                                        ForEach(VideoListLayout.allCases, id: \.self) { option in
                                            Label(option.displayName, systemImage: option.systemImage)
                                                .tag(option)
                                        }
                                    } label: {
                                        Text("viewOptions.layout")
                                    }
                                    .pickerStyle(.segmented)
                                    .listRowBackground(Color.clear)
                                    .listRowInsets(EdgeInsets(top: 8, leading: 0, bottom: 8, trailing: 0))

                                    // List-specific options
                                    if layout == .list {
                                        Picker("viewOptions.rowSize", selection: $rowStyle) {
                                            Text("viewOptions.rowSize.compact").tag(VideoRowStyle.compact)
                                            Text("viewOptions.rowSize.regular").tag(VideoRowStyle.regular)
                                            Text("viewOptions.rowSize.large").tag(VideoRowStyle.large)
                                        }
                                    }

                                    // Grid-specific options
                                    #if !os(tvOS)
                                    if layout == .grid {
                                        Stepper(
                                            "viewOptions.columns \(min(max(1, gridColumns), gridConfig.maxColumns))",
                                            value: $gridColumns,
                                            in: 1...gridConfig.maxColumns
                                        )
                                    }
                                    #endif

                                    Toggle("viewOptions.hideWatched", isOn: $hideWatched)

                                    Picker("viewOptions.channelStrip", selection: $channelStripSize) {
                                        ForEach(ChannelStripSize.allCases, id: \.self) { size in
                                            Text(size.displayName).tag(size)
                                        }
                                    }
                                }

                                #if !os(tvOS)
                                Section {
                                    NavigationLink {
                                        SubscriptionsSettingsView()
                                    } label: {
                                        Label(String(localized: "manageChannels.subscriptionsData"), systemImage: "person.2.badge.gearshape")
                                    }
                                }
                                #endif
                            }
                            .navigationTitle(String(localized: "subscriptions.viewOptions.title"))
                            #if os(iOS)
                            .navigationBarTitleDisplayMode(.inline)
                            #endif
                        }
                        .presentationDetents([.height(420), .large])
                        .presentationDragIndicator(.visible)
                        .liquidGlassSheetContent(sourceID: "subscriptionsViewOptions", in: sheetTransition)
                    }
                    .task {
                        await loadSubscriptionsAsync()
                        loadWatchEntries()
                    }
                    .onReceive(NotificationCenter.default.publisher(for: .subscriptionsDidChange)) { _ in
                        Task {
                            await loadSubscriptionsAsync()
                        }
                        // Subscription changes now trigger a full refresh via invalidation
                    }
                    .onReceive(NotificationCenter.default.publisher(for: .watchHistoryDidChange)) { _ in
                        loadWatchEntries()
                    }
                    .onChange(of: appEnvironment?.settingsManager.subscriptionAccount) { _, _ in
                        // Clear cache and refresh when subscription account changes
                        feedCache.handleAccountChange()
                        subscriptions = []
                        subscriptionsLoaded = false
                        Task {
                            guard let appEnvironment else { return }
                            await loadSubscriptionsAsync()
                            await feedCache.refresh(using: appEnvironment)
                        }
                    }
                    .task(id: instanceConfigurationID) {
                        LoggingService.shared.debug("SubscriptionsView task triggered, instanceConfigurationID: \(instanceConfigurationID)", category: .general)
                        await loadSubscriptionsAsync()

                        await feedCache.loadFromDiskIfNeeded()

                        let hasYatteeServer = appEnvironment?.instancesManager.instances.contains {
                            $0.type == .yatteeServer && $0.isEnabled
                        } ?? false

                        let cacheValid = feedCache.isCacheValid(using: appEnvironment?.settingsManager)
                        LoggingService.shared.debug(
                            "hasYatteeServer: \(hasYatteeServer), cacheValid: \(cacheValid), isLoading: \(feedCache.isLoading)",
                            category: .general
                        )

                        if hasYatteeServer {
                            LoggingService.shared.info("Yattee Server detected, forcing feed refresh", category: .general)
                            await loadFeed(forceRefresh: true)
                        } else if !cacheValid && !feedCache.isLoading {
                            LoggingService.shared.info("Cache invalid and not loading, refreshing feed", category: .general)
                            await loadFeed(forceRefresh: false)
                        } else {
                            LoggingService.shared.debug("Using cached feed, no refresh needed", category: .general)
                        }
                    }

                    // Bottom overlay for filter strip
                    VStack {
                        Spacer()

                        #if !os(tvOS)
                        if subscriptionsLoaded && subscriptions.count > 1 && channelStripSize != .disabled && !isShowingFullScreenError {
                            bottomFloatingFilterStrip
                                .transition(.move(edge: .bottom).combined(with: .opacity))
                        }
                        #endif
                    }
                }
                .onChange(of: selectedChannelID) { _, _ in
                    withAnimation {
                        proxy.scrollTo("top", anchor: .top)
                    }
                }
            }
            .onChange(of: geometry.size.width, initial: true) { _, newWidth in
                viewWidth = newWidth
            }
        }
    }

    // MARK: - List Layout

    private var listContent: some View {
        VideoListContainer(listStyle: listStyle, rowStyle: rowStyle) {
            // Header: status banner with scroll anchor
            feedStatusBanner
                .id("top")

            // Section header
            sectionHeaderView
        } content: {
            feedContentRows
        } footer: {
            // Bottom spacer for channel strip overlay (outside the card)
            if channelStripSize != .disabled && subscriptions.count > 1 && !isShowingFullScreenError {
                Color.clear.frame(height: channelStripSize.totalHeight)
            }
        }
    }

    /// Section header with proper padding for list style.
    private var sectionHeaderView: some View {
        HStack {
            feedSectionHeader
            Spacer()
        }
        .padding(.horizontal, listStyle == .inset ? 32 : 16)
        .padding(.top, 16)
        .padding(.bottom, 8)
    }

    /// Feed content rows or empty/loading states.
    @ViewBuilder
    private var feedContentRows: some View {
        if case .error(let feedError) = feedCache.feedLoadState, feedCache.videos.isEmpty {
            // Show specific error states
            switch feedError {
            case .yatteeServerRequired:
                yatteeServerRequiredView
            case .notAuthenticated:
                notAuthenticatedView
            case .networkError(let message):
                gridErrorView(message)
            }
        } else if feedCache.isLoading && feedCache.videos.isEmpty {
            gridLoadingView
        } else if let error = errorMessage, feedCache.videos.isEmpty {
            gridErrorView(error)
        } else if !feedCache.videos.isEmpty {
            if filteredVideos.isEmpty && selectedChannelID != nil {
                ContentUnavailableView {
                    Label(String(localized: "subscriptions.noVideosFromChannel"), systemImage: "video.slash")
                } description: {
                    Text(String(localized: "subscriptions.noVideosFromChannel.description"))
                }
                .frame(maxWidth: .infinity)
                .padding(.vertical, 40)
            } else {
                ForEach(Array(filteredVideos.enumerated()), id: \.element.id) { index, video in
                    VideoListRow(
                        isLast: index == filteredVideos.count - 1,
                        rowStyle: rowStyle,
                        listStyle: listStyle
                    ) {
                        VideoRowView(
                            video: video,
                            style: rowStyle,
                            watchProgress: watchProgress(for: video)
                        )
                        .tappableVideo(
                            video,
                            queueSource: subscriptionsQueueSource,
                            sourceLabel: String(localized: "queue.source.subscriptions"),
                            videoList: filteredVideos,
                            videoIndex: index,
                            loadMoreVideos: loadMoreSubscriptionsCallback
                        )
                    }
                    #if !os(tvOS)
                    .videoSwipeActions(video: video)
                    #endif
                }
                
                // Infinite scroll trigger for Invidious feed
                if feedCache.hasMorePages && !feedCache.isLoading {
                    Color.clear
                        .frame(height: 1)
                        .onAppear {
                            Task {
                                guard let appEnvironment else { return }
                                await feedCache.loadMoreInvidiousFeed(using: appEnvironment)
                            }
                        }
                }
            }
        } else if feedCache.hasLoadedOnce {
            gridEmptyView
        } else {
            gridLoadingView
        }
    }

    // MARK: - Grid Layout

    private var gridContent: some View {
        ScrollView {
            LazyVStack(spacing: 0) {
                feedStatusBanner
                    .id("top")

                // Section header
                HStack {
                    feedSectionHeader
                    Spacer()
                }
                .padding(.horizontal)
                .padding(.top, 8)
                .padding(.bottom, 12)

                // Content
                if case .error(let feedError) = feedCache.feedLoadState, feedCache.videos.isEmpty {
                    // Show specific error states
                    switch feedError {
                    case .yatteeServerRequired:
                        yatteeServerRequiredView
                    case .notAuthenticated:
                        notAuthenticatedView
                    case .networkError(let message):
                        gridErrorView(message)
                    }
                } else if feedCache.isLoading && feedCache.videos.isEmpty {
                    gridLoadingView
                } else if let error = errorMessage, feedCache.videos.isEmpty {
                    gridErrorView(error)
                } else if !feedCache.videos.isEmpty {
                    gridFeedContent
                } else if feedCache.hasLoadedOnce {
                    gridEmptyView
                } else {
                    gridLoadingView
                }

                // Bottom spacer for channel strip overlay
                if channelStripSize != .disabled && subscriptions.count > 1 && !isShowingFullScreenError {
                    Color.clear.frame(height: channelStripSize.totalHeight)
                }
            }
        }
    }

    // MARK: - Channel Filter Strip

    private var bottomFloatingFilterStrip: some View {
        ViewThatFits(in: .horizontal) {
            // Option 1: Non-scrolling centered layout (used when all chips fit)
            channelChipsHStack
                .padding(.horizontal, 12)
                .padding(.vertical, channelStripSize.verticalPadding)
                .clipShape(Capsule())
                #if os(tvOS)
                .background(Color.black.opacity(0.3))
                #else
                .glassBackground(.regular, in: .capsule, fallback: .regularMaterial)
                #endif

            // Option 2: Scrollable layout (used when chips overflow)
            ScrollView(.horizontal, showsIndicators: false) {
                channelChipsHStack
                    .padding(.horizontal, 12)
                    .padding(.vertical, channelStripSize.verticalPadding)
            }
            .clipShape(Capsule())
            #if os(tvOS)
            .background(Color.black.opacity(0.3))
            #else
            .glassBackground(.regular, in: .capsule, fallback: .regularMaterial)
            #endif
        }
        .padding(.horizontal, 16)
        .padding(.bottom, 8)
    }

    /// The HStack containing channel filter chips (extracted to avoid duplication).
    private var channelChipsHStack: some View {
        HStack(spacing: channelStripSize.chipSpacing) {
            ForEach(sortedSubscriptions, id: \.channelID) { subscription in
                ChannelFilterChip(
                    channelID: subscription.channelID,
                    name: subscription.name,
                    avatarURL: subscription.avatarURL,
                    serverURL: yatteeServerURL,
                    isSelected: selectedChannelID == subscription.channelID,
                    avatarSize: channelStripSize.avatarSize,
                    onTap: {
                        if selectedChannelID == subscription.channelID {
                            selectedChannelID = nil
                        } else {
                            selectedChannelID = subscription.channelID
                        }
                    },
                    onGoToChannel: {
                        appEnvironment?.navigationCoordinator.navigate(
                            to: .channel(subscription.channelID, subscription.contentSource)
                        )
                    },
                    onUnsubscribe: {
                        unsubscribeChannel(subscription.channelID)
                    },
                    authHeader: yatteeServerAuthHeader
                )
            }
        }
    }

    // MARK: - Content Views

    private var subscriptionsQueueSource: QueueSource {
        .subscriptions(continuation: nil)
    }

    @ViewBuilder
    private var gridFeedContent: some View {
        if filteredVideos.isEmpty && selectedChannelID != nil {
            ContentUnavailableView {
                Label(String(localized: "subscriptions.noVideosFromChannel"), systemImage: "video.slash")
            } description: {
                Text(String(localized: "subscriptions.noVideosFromChannel.description"))
            }
            .frame(maxWidth: .infinity)
            .padding(.vertical, 40)
        } else {
            VideoGridContent(columns: gridConfig.effectiveColumns) {
                ForEach(Array(filteredVideos.enumerated()), id: \.element.id) { index, video in
                    VideoCardView(
                        video: video,
                        watchProgress: watchProgress(for: video),
                        isCompact: gridConfig.isCompactCards
                    )
                    .tappableVideo(
                        video,
                        queueSource: subscriptionsQueueSource,
                        sourceLabel: String(localized: "queue.source.subscriptions"),
                        videoList: filteredVideos,
                        videoIndex: index,
                        loadMoreVideos: loadMoreSubscriptionsCallback
                    )
                }
            }
            
            // Infinite scroll trigger for Invidious feed
            if feedCache.hasMorePages && !feedCache.isLoading {
                Color.clear
                    .frame(height: 1)
                    .onAppear {
                        Task {
                            guard let appEnvironment else { return }
                            await feedCache.loadMoreInvidiousFeed(using: appEnvironment)
                        }
                    }
            }
        }
    }

    private var feedSectionHeader: some View {
        HStack {
            if feedCache.isLoading, let progress = feedCache.loadingProgress {
                Text("subscriptions.updatingChannels \(progress.loaded) \(progress.total)")
                    .monospacedDigit()
                    .foregroundStyle(.secondary)
            } else if let subscription = selectedSubscription {
                Button {
                    appEnvironment?.navigationCoordinator.navigate(
                        to: .channel(subscription.channelID, subscription.contentSource)
                    )
                } label: {
                    HStack(spacing: 4) {
                        Text(subscription.name)
                            .fontWeight(.semibold)
                        Image(systemName: "chevron.right")
                            .font(.caption)
                    }
                    .foregroundStyle(Color.accentColor)
                }
                .buttonStyle(.plain)
            } else {
                NavigationLink(value: NavigationDestination.manageChannels) {
                    HStack(spacing: 4) {
                        Text(String(localized: "subscriptions.allChannels"))
                            .fontWeight(.semibold)
                        Image(systemName: "chevron.right")
                            .font(.caption)
                    }
                    .foregroundStyle(Color.accentColor)
                }
                .buttonStyle(.plain)
            }
            Spacer()
        }
    }

    // MARK: - Loading/Error/Empty Views

    private var gridLoadingView: some View {
        VStack(spacing: 16) {
            ProgressView()

            if let progress = feedCache.loadingProgress {
                Text(verbatim: "\(progress.loaded)/\(progress.total)")
                    .font(.caption)
                    .foregroundStyle(.secondary)
                    .monospacedDigit()
            }
        }
        .frame(maxWidth: .infinity)
        .padding(.vertical, 40)
    }

    private var gridEmptyView: some View {
        ContentUnavailableView {
            Label(String(localized: "subscriptions.feed.title"), systemImage: "play.rectangle.on.rectangle")
        } description: {
            Text(String(localized: "subscriptions.empty.description"))
        }
        .frame(maxWidth: .infinity)
        .padding(.vertical, 40)
    }

    /// Empty state shown when Yattee Server is required but not configured.
    private var yatteeServerRequiredView: some View {
        ContentUnavailableView {
            Label(String(localized: "subscriptions.yatteeServerRequired.title"), systemImage: "server.rack")
        } description: {
            Text(String(localized: "subscriptions.yatteeServerRequired.description"))
        }
        .frame(maxWidth: .infinity)
        .padding(.vertical, 40)
    }

    /// Empty state shown when Invidious account is not authenticated.
    private var notAuthenticatedView: some View {
        ContentUnavailableView {
            Label(String(localized: "subscriptions.notAuthenticated.title"), systemImage: "person.crop.circle.badge.exclamationmark")
        } description: {
            Text(String(localized: "subscriptions.notAuthenticated.description"))
        }
        .frame(maxWidth: .infinity)
        .padding(.vertical, 40)
    }

    private func gridErrorView(_ error: String) -> some View {
        ContentUnavailableView {
            Label(String(localized: "common.error"), systemImage: "exclamationmark.triangle")
        } description: {
            Text(error)
        } actions: {
            Button(String(localized: "common.retry")) {
                Task { await loadFeed(forceRefresh: true) }
            }
        }
        .frame(maxWidth: .infinity)
        .padding(.vertical, 40)
    }

    // MARK: - Data Loading

    private func loadSubscriptions() {
        // For local account, load from DataManager
        // For Invidious, subscriptions will be loaded async in loadSubscriptionsAsync
        if appEnvironment?.settingsManager.subscriptionAccount.type == .local {
            subscriptions = dataManager?.subscriptions() ?? []
            subscriptionsLoaded = true
        }

        if let selectedID = selectedChannelID,
           !subscriptions.contains(where: { $0.channelID == selectedID }) {
            selectedChannelID = nil
        }
    }

    /// Loads subscriptions asynchronously from the current provider.
    /// For Invidious, this fetches from the API and creates temporary Subscription objects for UI.
    private func loadSubscriptionsAsync() async {
        guard let subscriptionService, let appEnvironment else { return }

        // For local account, just load from DataManager (fast)
        if appEnvironment.settingsManager.subscriptionAccount.type == .local {
            subscriptions = dataManager?.subscriptions() ?? []
            subscriptionsLoaded = true
            return
        }

        // For Invidious, fetch from API
        do {
            let channels = try await subscriptionService.fetchSubscriptions()
            // Convert channels to Subscription objects for UI (not persisted)
            subscriptions = channels.map { Subscription.from(channel: $0) }
            subscriptionsLoaded = true
        } catch {
            LoggingService.shared.error(
                "Failed to load subscriptions: \(error.localizedDescription)",
                category: .general
            )
            subscriptions = []
            subscriptionsLoaded = true
        }
    }

    private func loadWatchEntries() {
        watchEntriesMap = dataManager?.watchEntriesMap() ?? [:]
    }

    private func loadFeed(forceRefresh: Bool) async {
        guard let appEnvironment else { return }

        if !forceRefresh && feedCache.isCacheValid(using: appEnvironment.settingsManager) {
            return
        }

        errorMessage = nil
        await feedCache.refresh(using: appEnvironment)
    }

    private func unsubscribeChannel(_ channelID: String) {
        Task {
            do {
                try await subscriptionService?.unsubscribe(from: channelID)
                // Remove from local list immediately for responsiveness
                subscriptions.removeAll { $0.channelID == channelID }
            } catch {
                LoggingService.shared.error(
                    "Failed to unsubscribe: \(error.localizedDescription)",
                    category: .general
                )
            }
        }
    }

    @Sendable
    private func loadMoreSubscriptionsCallback() async throws -> ([Video], String?) {
        return ([], nil)
    }
}

// MARK: - Preview

#Preview("With Subscriptions") {
    PreviewWrapper()
}

private struct PreviewWrapper: View {
    let dataManager: DataManager
    let previewEnvironment: AppEnvironment

    init() {
        let dataManager = try! DataManager.preview()

        let channel1 = Channel(
            id: ChannelID(source: .global(provider: ContentSource.youtubeProvider), channelID: "UC1"),
            name: "Apple Developer",
            thumbnailURL: nil
        )
        let channel2 = Channel(
            id: ChannelID(source: .global(provider: ContentSource.youtubeProvider), channelID: "UC2"),
            name: "Marques Brownlee",
            thumbnailURL: nil
        )
        let channel3 = Channel(
            id: ChannelID(source: .global(provider: ContentSource.youtubeProvider), channelID: "UC3"),
            name: "Music Channel",
            thumbnailURL: nil
        )

        dataManager.subscribe(to: channel1)
        dataManager.subscribe(to: channel2)
        dataManager.subscribe(to: channel3)

        self.dataManager = dataManager
        self.previewEnvironment = AppEnvironment(dataManager: dataManager)

        let cache = SubscriptionFeedCache.shared
        cache.videos = [
            Video(
                id: VideoID(source: .global(provider: ContentSource.youtubeProvider), videoID: "video1"),
                title: "SwiftUI Tutorial: Building Amazing Apps",
                description: "Learn how to build amazing apps with SwiftUI",
                author: Author(id: "UC1", name: "Apple Developer"),
                duration: 600,
                publishedAt: Date().addingTimeInterval(-3600),
                publishedText: "1 hour ago",
                viewCount: 10000,
                likeCount: 500,
                thumbnails: [],
                isLive: false,
                isUpcoming: false,
                scheduledStartTime: nil
            ),
            Video(
                id: VideoID(source: .global(provider: ContentSource.youtubeProvider), videoID: "video2"),
                title: "Tech Review: Latest Innovations",
                description: "Reviewing the latest tech innovations",
                author: Author(id: "UC2", name: "Marques Brownlee"),
                duration: 900,
                publishedAt: Date().addingTimeInterval(-7200),
                publishedText: "2 hours ago",
                viewCount: 50000,
                likeCount: 2000,
                thumbnails: [],
                isLive: false,
                isUpcoming: false,
                scheduledStartTime: nil
            ),
            Video(
                id: VideoID(source: .global(provider: ContentSource.youtubeProvider), videoID: "video3"),
                title: "Music Production Tips and Tricks",
                description: "Professional music production techniques",
                author: Author(id: "UC3", name: "Music Channel"),
                duration: 450,
                publishedAt: Date().addingTimeInterval(-10800),
                publishedText: "3 hours ago",
                viewCount: 5000,
                likeCount: 250,
                thumbnails: [],
                isLive: false,
                isUpcoming: false,
                scheduledStartTime: nil
            )
        ]
        cache.hasLoadedOnce = true
        cache.lastUpdated = Date()
    }

    var body: some View {
        NavigationStack {
            SubscriptionsView()
        }
        .appEnvironment(previewEnvironment)
    }
}

#Preview("Empty") {
    NavigationStack {
        SubscriptionsView()
    }
    .appEnvironment(.preview)
}
