//
//  HistoryListView.swift
//  Yattee
//
//  Full page view for watch history.
//

import SwiftUI

struct HistoryListView: View {
    @Environment(\.appEnvironment) private var appEnvironment
    @Namespace private var sheetTransition
    @State private var history: [WatchEntry] = []
    @State private var videos: [Video] = []  // Cached Video conversions to avoid repeated toVideo() calls
    @State private var showingClearConfirmation = false
    @State private var selectedClearOption: ClearHistoryOption?

    // View options (persisted)
    @AppStorage("history.layout") private var layout: VideoListLayout = .list
    @AppStorage("history.rowStyle") private var rowStyle: VideoRowStyle = .regular
    @AppStorage("history.gridColumns") private var gridColumns = 2

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

    // UI state
    @State private var showViewOptions = false
    @State private var viewWidth: CGFloat = 0
    @State private var searchText = ""

    // Grid layout configuration
    private var gridConfig: GridLayoutConfiguration {
        GridLayoutConfiguration(viewWidth: viewWidth, gridColumns: gridColumns)
    }

    private var dataManager: DataManager? { appEnvironment?.dataManager }

    /// History entries filtered by search text.
    private var filteredHistory: [WatchEntry] {
        guard !searchText.isEmpty else { return history }
        let query = searchText.lowercased()
        return history.filter { entry in
            entry.title.lowercased().contains(query) ||
            entry.authorName.lowercased().contains(query) ||
            entry.videoID.lowercased().contains(query)
        }
    }

    /// Pre-computed Video objects for filtered history entries.
    private var filteredVideos: [Video] {
        guard !searchText.isEmpty else { return videos }
        let filteredIDs = Set(filteredHistory.map { $0.videoID })
        return zip(history, videos)
            .filter { filteredIDs.contains($0.0.videoID) }
            .map { $0.1 }
    }

    /// Gets the watch progress (0.0-1.0) for a watch entry, or nil if finished.
    private func watchProgress(for entry: WatchEntry) -> Double? {
        let progress = entry.progress
        // Only show progress bar for partially watched videos
        return progress > 0 && progress < 1 ? progress : nil
    }

    /// Queue source for history.
    private var historyQueueSource: QueueSource {
        .manual
    }

    /// Stub callback for video queue continuation.
    /// History doesn't support server-side pagination,
    /// so this returns empty to prevent errors.
    @Sendable
    private func loadMoreHistoryCallback() async throws -> ([Video], String?) {
        // History is fully loaded on initial fetch
        // No pagination support available
        return ([], nil)
    }

    private var viewOptionsSheetContent: some View {
        ViewOptionsSheet(
            layout: $layout,
            rowStyle: $rowStyle,
            gridColumns: $gridColumns,
            hideWatched: nil,  // No hide watched for history view
            maxGridColumns: gridConfig.maxColumns
        )
    }

    /// View options button lives on the leading edge on macOS, trailing elsewhere.
    private var viewOptionsPlacement: ToolbarItemPlacement {
        #if os(macOS)
        .navigation
        #else
        .primaryAction
        #endif
    }

    var body: some View {
        GeometryReader { geometry in
            #if os(tvOS)
            VStack(spacing: 0) {
                // tvOS: Inline search field and action button for better focus navigation
                HStack(spacing: 24) {
                    TextField("search.history.placeholder", text: $searchText)
                        .textFieldStyle(.plain)

                    Button {
                        showViewOptions = true
                    } label: {
                        Label(String(localized: "viewOptions.title"), systemImage: "slider.horizontal.3")
                    }

                    if !history.isEmpty {
                        Menu {
                            ForEach(ClearHistoryOption.allCases, id: \.self) { option in
                                Button(role: .destructive) {
                                    selectedClearOption = option
                                    showingClearConfirmation = true
                                } label: {
                                    Label(option.localizedTitle, systemImage: option.systemImage)
                                }
                            }
                        } label: {
                            Label(String(localized: "home.history.clear"), systemImage: "trash")
                        }
                    }
                }
                .focusSection()
                .padding(.horizontal, 48)
                .padding(.top, 20)
                .padding(.bottom, 20)

                // Content
                Group {
                    if filteredHistory.isEmpty {
                        emptyView
                    } else {
                        switch layout {
                        case .list:
                            listContent
                        case .grid:
                            gridContent
                        }
                    }
                }
                .focusSection()
            }
            .onChange(of: geometry.size.width, initial: true) { _, newWidth in
                viewWidth = newWidth
            }
            #else
            Group {
                if filteredHistory.isEmpty {
                    // Empty state
                    emptyView
                } else {
                    // Content based on layout
                    switch layout {
                    case .list:
                        listContent
                    case .grid:
                        gridContent
                    }
                }
            }
            .onChange(of: geometry.size.width, initial: true) { _, newWidth in
                viewWidth = newWidth
            }
            #endif
        }
        #if !os(tvOS)
        .navigationTitle(String(localized: "home.history.title"))
        .toolbarTitleDisplayMode(.inlineLarge)
        .searchable(text: $searchText, prompt: Text(String(localized: "history.search.placeholder")))
        .toolbar {
            #if os(macOS)
            // Pin the trailing group (search field + toolbar buttons) to the right edge,
            // matching the global Search view.
            if #available(macOS 26, *) {
                ToolbarSpacer(.flexible, placement: .primaryAction)
            }
            #endif
            // View options button (always visible)
            ToolbarItem(placement: viewOptionsPlacement) {
                Button {
                    showViewOptions = true
                } label: {
                    Label(String(localized: "viewOptions.title"), systemImage: "slider.horizontal.3")
                }
                .liquidGlassTransitionSource(id: "historyViewOptions", in: sheetTransition)
                #if os(macOS)
                .popover(isPresented: $showViewOptions, arrowEdge: .bottom) {
                    viewOptionsSheetContent
                }
                #endif
            }

            // Clear history menu (only when not empty)
            if !history.isEmpty {
                ToolbarItem(placement: .primaryAction) {
                    Menu {
                        ForEach(ClearHistoryOption.allCases, id: \.self) { option in
                            Button(role: .destructive) {
                                selectedClearOption = option
                                showingClearConfirmation = true
                            } label: {
                                Label(option.localizedTitle, systemImage: option.systemImage)
                            }
                        }
                    } label: {
                        Image(systemName: "trash")
                    }
                }
            }
        }
        #endif
        #if !os(macOS)
        .sheet(isPresented: $showViewOptions) {
            viewOptionsSheetContent
                .liquidGlassSheetContent(sourceID: "historyViewOptions", in: sheetTransition)
        }
        #endif
        .confirmationDialog(
            confirmationTitle,
            isPresented: $showingClearConfirmation,
            titleVisibility: .visible
        ) {
            Button(String(localized: "home.history.clear"), role: .destructive) {
                clearHistory()
            }
            Button(String(localized: "common.cancel"), role: .cancel) {
                selectedClearOption = nil
            }
        }
        .presentationCompactAdaptation(.sheet)
        .onAppear {
            loadHistory()
        }
        .onReceive(NotificationCenter.default.publisher(for: .watchHistoryDidChange)) { _ in
            loadHistory()
        }
    }

    // MARK: - Empty View

    @ViewBuilder
    private var emptyView: some View {
        if !searchText.isEmpty {
            ContentUnavailableView.search(text: searchText)
            .frame(maxWidth: .infinity, maxHeight: .infinity)
        } else {
            ContentUnavailableView {
                Label(String(localized: "home.history.title"), systemImage: "clock")
            } description: {
                Text(String(localized: "home.history.empty"))
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
        }
    }

    // MARK: - List Layout

    private var listContent: some View {
        VideoListContainer(listStyle: listStyle, rowStyle: rowStyle) {
            // Header spacer for top padding
            Spacer()
                .frame(height: 16)
        } content: {
            ForEach(Array(filteredHistory.enumerated()), id: \.element.videoID) { index, entry in
                let video = filteredVideos[index]
                VideoListRow(
                    isLast: index == filteredHistory.count - 1,
                    rowStyle: rowStyle,
                    listStyle: listStyle
                ) {
                    entryRowView(entry: entry, index: index)
                }
                #if !os(tvOS)
                .videoSwipeActions(
                    video: video,
                    fixedActions: [
                        SwipeAction(
                            symbolImage: "trash.fill",
                            tint: .white,
                            background: .red
                        ) { reset in
                            removeEntry(entry)
                            reset()
                        }
                    ]
                )
                #endif
            }
        }
    }

    /// Reusable row view for a history entry.
    @ViewBuilder
    private func entryRowView(entry: WatchEntry, index: Int) -> some View {
        let video = filteredVideos[index]
        VideoRowView(
            video: video,
            style: rowStyle,
            watchProgress: watchProgress(for: entry),
            customMetadata: entry.isFinished || entry.isLive ? nil : String(localized: "home.history.remaining \(entry.remainingTime)")
        )
        .tappableVideo(
            video,
            startTime: entry.watchedSeconds,
            customActions: [
                VideoContextAction(
                    String(localized: "home.history.remove"),
                    systemImage: "trash",
                    role: .destructive,
                    action: { removeEntry(entry) }
                )
            ],
            context: .history,
            queueSource: historyQueueSource,
            sourceLabel: String(localized: "queue.source.history"),
            videoList: filteredVideos,
            videoIndex: index,
            loadMoreVideos: loadMoreHistoryCallback
        )
    }

    // MARK: - Grid Layout

    private var gridContent: some View {
        ScrollView {
            VideoGridContent(columns: gridConfig.effectiveColumns) {
                ForEach(Array(filteredHistory.enumerated()), id: \.element.videoID) { index, entry in
                    let video = filteredVideos[index]
                    VideoCardView(
                        video: video,
                        watchProgress: watchProgress(for: entry),
                        isCompact: gridConfig.isCompactCards
                    )
                    .tappableVideo(
                        video,
                        startTime: entry.watchedSeconds,
                        customActions: [
                            VideoContextAction(
                                String(localized: "home.history.remove"),
                                systemImage: "trash",
                                role: .destructive,
                                action: { removeEntry(entry) }
                            )
                        ],
                        context: .history,
                        queueSource: historyQueueSource,
                        sourceLabel: String(localized: "queue.source.history"),
                        videoList: filteredVideos,
                        videoIndex: index,
                        loadMoreVideos: loadMoreHistoryCallback
                    )
                }
            }
        }
        #if os(tvOS)
        .scrollClipDisabled()
        #endif
    }

    private var confirmationTitle: String {
        guard let option = selectedClearOption else {
            return String(localized: "home.history.clear.confirm")
        }

        if option == .all {
            return String(localized: "home.history.clear.confirm")
        }

        return String(localized: "home.history.clear.confirm.timeRange \(option.localizedTitle)")
    }

    private func loadHistory() {
        history = dataManager?.watchHistory(limit: 10000) ?? []
        videos = history.map { $0.toVideo() }  // Pre-compute all Video conversions once
    }

    private func removeEntry(_ entry: WatchEntry) {
        dataManager?.removeFromHistory(videoID: entry.videoID)
        loadHistory()
    }

    private func clearHistory() {
        guard let option = selectedClearOption else { return }

        if option == .all {
            dataManager?.clearWatchHistory()
        } else if let date = option.cutoffDate {
            dataManager?.clearWatchHistory(since: date)
        }

        selectedClearOption = nil
        loadHistory()
    }
}

// MARK: - Clear History Options

private enum ClearHistoryOption: CaseIterable {
    case lastHour
    case lastDay
    case lastWeek
    case lastMonth
    case all

    var localizedTitle: String {
        switch self {
        case .lastHour:
            return String(localized: "home.history.clear.lastHour")
        case .lastDay:
            return String(localized: "home.history.clear.lastDay")
        case .lastWeek:
            return String(localized: "home.history.clear.lastWeek")
        case .lastMonth:
            return String(localized: "home.history.clear.lastMonth")
        case .all:
            return String(localized: "home.history.clear.all")
        }
    }

    var systemImage: String {
        switch self {
        case .lastHour:
            return "clock"
        case .lastDay:
            return "sun.max"
        case .lastWeek:
            return "calendar"
        case .lastMonth:
            return "calendar.badge.clock"
        case .all:
            return "trash"
        }
    }

    var cutoffDate: Date? {
        let calendar = Calendar.current
        let now = Date()

        switch self {
        case .lastHour:
            return calendar.date(byAdding: .hour, value: -1, to: now)
        case .lastDay:
            return calendar.date(byAdding: .day, value: -1, to: now)
        case .lastWeek:
            return calendar.date(byAdding: .weekOfYear, value: -1, to: now)
        case .lastMonth:
            return calendar.date(byAdding: .month, value: -1, to: now)
        case .all:
            return nil
        }
    }
}
