//
//  MediaSourcesView.swift
//  Yattee
//
//  View for browsing all configured sources (instances and media sources).
//

import SwiftUI

struct MediaSourcesView: View {
    @Environment(\.appEnvironment) private var appEnvironment
    @State private var sourceToEdit: UnifiedSource?
    @State private var showingAddSheet = false
    @State private var showingDeleteConfirmation = false
    @State private var pendingDeleteInstance: Instance?
    @State private var pendingDeleteSource: MediaSource?

    private var instancesManager: InstancesManager? {
        appEnvironment?.instancesManager
    }

    private var mediaSourcesManager: MediaSourcesManager? {
        appEnvironment?.mediaSourcesManager
    }

    private var sourcesSettings: SourcesSettings? {
        appEnvironment?.sourcesSettings
    }

    private var isEmpty: Bool {
        (instancesManager?.enabledInstances.isEmpty ?? true) &&
        (mediaSourcesManager?.enabledSources.isEmpty ?? true)
    }

    private var listStyle: VideoListStyle {
        appEnvironment?.settingsManager.listStyle ?? .inset
    }

    var body: some View {
        Group {
            if isEmpty {
                ContentUnavailableView {
                    Label(String(localized: "sources.empty.title"), systemImage: "server.rack")
                } description: {
                    Text(String(localized: "sources.empty.description"))
                } actions: {
                    Button(String(localized: "sources.addSource")) {
                        showingAddSheet = true
                    }
                    .buttonStyle(.borderedProminent)
                }
                .frame(maxWidth: .infinity, maxHeight: .infinity)
            } else {
                sourcesList
            }
        }
        #if !os(tvOS)
        .navigationTitle(String(localized: "sources.title"))
        .toolbarTitleDisplayMode(.inlineLarge)
        .toolbar {
            ToolbarItem(placement: .primaryAction) {
                Button {
                    showingAddSheet = true
                } label: {
                    Label(String(localized: "sources.addSource"), systemImage: "plus")
                }
            }
            ToolbarItem(placement: .primaryAction) {
                if let settings = sourcesSettings {
                    sortAndGroupMenu(settings)
                }
            }
        }
        #endif
        .sheet(item: $sourceToEdit) { source in
            EditSourceView(source: source)
        }
        .sheet(isPresented: $showingAddSheet) {
            AddSourceView()
        }
        .confirmationDialog(
            deleteConfirmationMessage,
            isPresented: $showingDeleteConfirmation,
            titleVisibility: .visible
        ) {
            Button(String(localized: "common.delete"), role: .destructive) {
                confirmDelete()
            }
            Button(String(localized: "common.cancel"), role: .cancel) {
                pendingDeleteInstance = nil
                pendingDeleteSource = nil
            }
        }
        .presentationCompactAdaptation(.sheet)
    }

    // MARK: - Private

    private var deleteConfirmationMessage: String {
        if let instance = pendingDeleteInstance {
            return String(localized: "sources.delete.confirmation.single \(instance.displayName)")
        } else if let source = pendingDeleteSource {
            return String(localized: "sources.delete.confirmation.single \(source.name)")
        }
        return String(localized: "sources.delete.confirmation")
    }

    private func confirmDelete() {
        if let instance = pendingDeleteInstance {
            instancesManager?.remove(instance)
            pendingDeleteInstance = nil
        }
        if let source = pendingDeleteSource {
            mediaSourcesManager?.remove(source)
            pendingDeleteSource = nil
        }
    }

    @ViewBuilder
    private func sectionHeader(_ title: String) -> some View {
        Text(title)
            .fontWeight(.semibold)
            .foregroundStyle(.secondary)
            .frame(maxWidth: .infinity, alignment: .leading)
            .padding(.horizontal, listStyle == .inset ? 32 : 16)
            .padding(.top, 16)
            .padding(.bottom, 8)
    }

    private var sourcesList: some View {
        (listStyle == .inset ? ListBackgroundStyle.grouped.color : ListBackgroundStyle.plain.color)
            .ignoresSafeArea()
            .overlay(
                ScrollView {
                    LazyVStack(spacing: 0) {
                        if let settings = sourcesSettings, !settings.groupByType {
                            // Ungrouped: All sources in one section
                            allSourcesSection(settings)
                        } else {
                            // Grouped by type (default)
                            groupedSourcesSections
                        }
                    }
                }
            )
    }

    @ViewBuilder
    private var groupedSourcesSections: some View {
        let settings = sourcesSettings

        // Instances section
        if let manager = instancesManager, !manager.enabledInstances.isEmpty {
            sectionHeader(String(localized: "sources.section.remoteServers"))

            let sortedInstances = settings?.sorted(manager.enabledInstances) ?? manager.enabledInstances
            sectionCard {
                instancesSectionContent(sortedInstances)
            }
        }

        // Media sources section
        if let manager = mediaSourcesManager, !manager.enabledSources.isEmpty {
            sectionHeader(String(localized: "sources.section.fileSources"))

            let sortedSources = settings?.sorted(manager.enabledSources) ?? manager.enabledSources
            sectionCard {
                fileSourcesSectionContent(sortedSources)
            }
        }
    }

    @ViewBuilder
    private func allSourcesSection(_ settings: SourcesSettings) -> some View {
        let sortedSources = allUnifiedSources(settings: settings)

        if !sortedSources.isEmpty {
            sectionHeader(String(localized: "sources.section.allSources"))

            sectionCard {
                ForEach(Array(sortedSources.enumerated()), id: \.element.id) { index, item in
                    let isLast = index == sortedSources.count - 1

                    switch item {
                    case .instance(let instance):
                        instanceRowView(instance, isLast: isLast)
                    case .mediaSource(let source):
                        fileSourceRowView(source, isLast: isLast)
                    }
                }
            }
        }
    }

    private func allUnifiedSources(settings: SourcesSettings) -> [UnifiedSourceItem] {
        let instances = instancesManager?.enabledInstances ?? []
        let mediaSources = mediaSourcesManager?.enabledSources ?? []

        var allSources: [UnifiedSourceItem] = []
        allSources.append(contentsOf: instances.map { UnifiedSourceItem.instance($0) })
        allSources.append(contentsOf: mediaSources.map { UnifiedSourceItem.mediaSource($0) })

        return sortUnifiedSources(allSources, settings: settings)
    }

    private func sortUnifiedSources(_ sources: [UnifiedSourceItem], settings: SourcesSettings) -> [UnifiedSourceItem] {
        sources.sorted { first, second in
            let comparison: Bool
            switch settings.sortOption {
            case .name:
                comparison = first.displayName.localizedCaseInsensitiveCompare(second.displayName) == .orderedAscending
            case .type:
                comparison = first.typeDisplayName.localizedCaseInsensitiveCompare(second.typeDisplayName) == .orderedAscending
            case .dateAdded:
                comparison = first.dateAdded < second.dateAdded
            }
            return settings.sortDirection == .ascending ? comparison : !comparison
        }
    }

    // MARK: - Sort and Group Menu

    @ViewBuilder
    private func sortAndGroupMenu(_ settings: SourcesSettings) -> some View {
        Menu {
            // Sort options
            Section {
                Picker(selection: Binding(
                    get: { settings.sortOption },
                    set: { settings.sortOption = $0 }
                )) {
                    ForEach(settings.availableSortOptions, id: \.self) { option in
                        Label(option.displayName, systemImage: option.systemImage)
                            .tag(option)
                    }
                } label: {
                    Label(String(localized: "sources.sort.title"), systemImage: "arrow.up.arrow.down")
                }

                // Sort direction
                Button {
                    settings.sortDirection.toggle()
                } label: {
                    Label(
                        settings.sortDirection == .ascending
                            ? String(localized: "sources.sort.ascending")
                            : String(localized: "sources.sort.descending"),
                        systemImage: settings.sortDirection.systemImage
                    )
                }
            }

            // Grouping
            Section {
                Toggle(isOn: Binding(
                    get: { settings.groupByType },
                    set: {
                        settings.groupByType = $0
                        // Reset to name sort if type sort was selected and grouping is now enabled
                        if $0 && settings.sortOption == .type {
                            settings.sortOption = .name
                        }
                    }
                )) {
                    Label(String(localized: "sources.groupByType"), systemImage: "rectangle.3.group")
                }
            }
        } label: {
            Label(String(localized: "sources.sortAndGroup"), systemImage: "line.3.horizontal.decrease.circle")
        }
    }

    @ViewBuilder
    private func sectionCard<Content: View>(@ViewBuilder content: () -> Content) -> some View {
        if listStyle == .inset {
            LazyVStack(spacing: 0) {
                content()
            }
            .background(ListBackgroundStyle.card.color)
            .clipShape(RoundedRectangle(cornerRadius: 10))
            .padding(.horizontal, 16)
            .padding(.bottom, 16)
        } else {
            LazyVStack(spacing: 0) {
                content()
            }
            .padding(.bottom, 16)
        }
    }

    @ViewBuilder
    private func instancesSectionContent(_ instances: [Instance]) -> some View {
        ForEach(Array(instances.enumerated()), id: \.element.id) { index, instance in
            let isLastInSection = index == instances.count - 1

            instanceRowView(instance, isLast: isLastInSection)
        }
    }

    @ViewBuilder
    private func instanceRowView(_ instance: Instance, isLast: Bool) -> some View {
        SourceListRow(isLast: isLast, listStyle: listStyle) {
            NavigationLink(value: NavigationDestination.instanceBrowse(instance)) {
                instanceRow(instance)
            }
            .foregroundStyle(.primary)
        }
        .swipeActions {
            SwipeAction(symbolImage: "pencil", tint: .white, background: .orange) { reset in
                sourceToEdit = .remoteServer(instance)
                reset()
            }
            SwipeAction(symbolImage: "trash", tint: .white, background: .red) { reset in
                pendingDeleteInstance = instance
                showingDeleteConfirmation = true
                reset()
            }
        }
        .contextMenu {
            Button {
                sourceToEdit = .remoteServer(instance)
            } label: {
                Label(String(localized: "common.edit"), systemImage: "pencil")
            }

            Button(role: .destructive) {
                pendingDeleteInstance = instance
                showingDeleteConfirmation = true
            } label: {
                Label(String(localized: "common.delete"), systemImage: "trash")
            }
        }
    }

    @ViewBuilder
    private func fileSourcesSectionContent(_ sources: [MediaSource]) -> some View {
        ForEach(Array(sources.enumerated()), id: \.element.id) { index, source in
            let isLastInSection = index == sources.count - 1

            fileSourceRowView(source, isLast: isLastInSection)
        }
    }

    @ViewBuilder
    private func fileSourceRowView(_ source: MediaSource, isLast: Bool) -> some View {
        let needsPassword = mediaSourcesManager?.needsPassword(for: source) ?? false

        SourceListRow(isLast: isLast, listStyle: listStyle) {
            if needsPassword {
                Button {
                    sourceToEdit = .fileSource(source)
                } label: {
                    mediaSourceRow(source, needsPassword: true)
                }
                .foregroundStyle(.primary)
            } else {
                NavigationLink(value: NavigationDestination.mediaBrowser(source, path: "/")) {
                    mediaSourceRow(source, needsPassword: false)
                }
                .foregroundStyle(.primary)
            }
        }
        .swipeActions {
            SwipeAction(symbolImage: "pencil", tint: .white, background: .orange) { reset in
                sourceToEdit = .fileSource(source)
                reset()
            }
            SwipeAction(symbolImage: "trash", tint: .white, background: .red) { reset in
                pendingDeleteSource = source
                showingDeleteConfirmation = true
                reset()
            }
        }
        .contextMenu {
            Button {
                sourceToEdit = .fileSource(source)
            } label: {
                Label(String(localized: "common.edit"), systemImage: "pencil")
            }

            Button(role: .destructive) {
                pendingDeleteSource = source
                showingDeleteConfirmation = true
            } label: {
                Label(String(localized: "common.delete"), systemImage: "trash")
            }
        }
    }

    private func instanceRow(_ instance: Instance) -> some View {
        HStack(spacing: 12) {
            Image(systemName: instance.type.systemImage)
                .font(.title2)
                .foregroundStyle(.tint)
                .frame(width: 32)

            VStack(alignment: .leading, spacing: 2) {
                Text(instance.displayName)
                    .font(.headline)
                    .foregroundStyle(.primary)

                Text("\(instance.type.displayName) - \(instance.url.host ?? instance.url.absoluteString)")
                    .font(.caption)
                    .foregroundStyle(.secondary)
            }

            Spacer()

            Image(systemName: "chevron.right")
                .font(.caption)
                .foregroundStyle(.secondary)
        }
    }

    private func mediaSourceRow(_ source: MediaSource, needsPassword: Bool) -> some View {
        HStack(spacing: 12) {
            Image(systemName: source.type.systemImage)
                .font(.title2)
                .foregroundStyle(.tint)
                .frame(width: 32)

            VStack(alignment: .leading, spacing: 2) {
                Text(source.name)
                    .font(.headline)
                    .foregroundStyle(.primary)

                Text("\(source.type.displayName) - \(source.urlDisplayString)")
                    .font(.caption)
                    .foregroundStyle(.secondary)

                if needsPassword {
                    Label(String(localized: "sources.status.authRequired"), systemImage: "key.fill")
                        .font(.caption2)
                        .foregroundStyle(.orange)
                }
            }

            Spacer()

            Image(systemName: "chevron.right")
                .font(.caption)
                .foregroundStyle(.secondary)
        }
    }
}

// MARK: - Unified Source Item

/// Unified wrapper for sorting instances and media sources together.
private enum UnifiedSourceItem: Identifiable {
    case instance(Instance)
    case mediaSource(MediaSource)

    var id: String {
        switch self {
        case .instance(let instance):
            return "instance-\(instance.id.uuidString)"
        case .mediaSource(let source):
            return "source-\(source.id.uuidString)"
        }
    }

    var displayName: String {
        switch self {
        case .instance(let instance):
            return instance.displayName
        case .mediaSource(let source):
            return source.name
        }
    }

    var typeDisplayName: String {
        switch self {
        case .instance(let instance):
            return instance.type.displayName
        case .mediaSource(let source):
            return source.type.displayName
        }
    }

    var dateAdded: Date {
        switch self {
        case .instance(let instance):
            return instance.dateAdded
        case .mediaSource(let source):
            return source.dateAdded
        }
    }
}

// MARK: - Preview

#Preview {
    NavigationStack {
        MediaSourcesView()
    }
    .appEnvironment(.preview)
}
