//
//  SubscriptionsSettingsView.swift
//  Yattee
//
//  Settings for importing and exporting subscriptions.
//

import SwiftUI
import UniformTypeIdentifiers

#if !os(tvOS)

// MARK: - Export File Wrapper

private struct ExportFile: Identifiable {
    let id = UUID()
    let url: URL
}

struct SubscriptionsSettingsView: View {
    @Environment(\.appEnvironment) private var appEnvironment

    // Account selection state
    @State private var pendingAccountChange: SubscriptionAccount?
    @State private var showingAccountSwitchConfirmation = false

    // Import state
    @State private var showingImportPicker = false
    @State private var isImporting = false
    @State private var importResult: (imported: Int, skipped: Int, format: String)?
    @State private var showingImportResult = false
    @State private var importError: String?
    @State private var showingImportError = false

    // Export state
    @State private var selectedExportFormat: SubscriptionExportFormat = .json
    @State private var exportFile: ExportFile?
    @State private var isExporting = false

    // Account subscription count (provider-scoped, fetched for server accounts)
    @State private var accountSubscriptionCount: Int?

    // Local data deletion state
    @State private var showingDeleteLocalConfirmation = false

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

    private var settingsManager: SettingsManager? {
        appEnvironment?.settingsManager
    }

    private var validator: SubscriptionAccountValidator? {
        appEnvironment?.subscriptionAccountValidator
    }

    private var subscriptionService: SubscriptionService? {
        appEnvironment?.subscriptionService
    }

    /// Subscription count of the active account (local store or server).
    private var subscriptionCount: Int {
        accountSubscriptionCount ?? subscriptionService?.cachedSubscriptionCount ?? 0
    }

    /// Count of subscriptions in the local store, shown in the local-data section.
    private var localSubscriptionCount: Int {
        dataManager?.subscriptionCount ?? 0
    }

    private var currentAccount: SubscriptionAccount {
        settingsManager?.subscriptionAccount ?? .local
    }

    var body: some View {
        List {
            accountSection
            if validator?.hasAvailableAccounts == true {
                importSection
                exportSection
                localDataSection
            }
        }
        .task(id: currentAccount) {
            await refreshAccountSubscriptionCount()
        }
        .navigationTitle(String(localized: "settings.subscriptions.title"))
        #if os(iOS)
        .navigationBarTitleDisplayMode(.inline)
        .sheet(isPresented: $showingImportPicker) {
            SubscriptionFilePickerView { url in
                handleImportedFile(url)
            }
        }
        .sheet(item: $exportFile) { file in
            ShareSheet(items: [file.url])
        }
        #endif
        .alert(
            String(localized: "settings.subscriptions.import.success.title"),
            isPresented: $showingImportResult
        ) {
            Button(String(localized: "common.ok")) {}
        } message: {
            if let result = importResult {
                Text(String(localized: "settings.subscriptions.import.success.message \(result.imported) \(result.skipped) \(result.format)"))
            }
        }
        .alert(
            String(localized: "settings.subscriptions.import.error.title"),
            isPresented: $showingImportError
        ) {
            Button(String(localized: "common.ok")) {}
        } message: {
            if let error = importError {
                Text(error)
            }
        }
        .confirmationDialog(
            String(localized: "settings.subscriptions.account.switch.title"),
            isPresented: $showingAccountSwitchConfirmation,
            titleVisibility: .visible
        ) {
            Button(String(localized: "settings.subscriptions.account.switch.action")) {
                confirmAccountSwitch()
            }
            Button(String(localized: "common.cancel"), role: .cancel) {
                cancelAccountSwitch()
            }
        } message: {
            Text(String(localized: "settings.subscriptions.account.switch.message"))
        }
        .confirmationDialog(
            String(localized: "settings.subscriptions.localData.delete.confirmation.title \(localSubscriptionCount)"),
            isPresented: $showingDeleteLocalConfirmation,
            titleVisibility: .visible
        ) {
            Button(String(localized: "settings.subscriptions.localData.delete.confirmation.action"), role: .destructive) {
                deleteLocalSubscriptions()
            }
        } message: {
            Text(String(localized: "settings.subscriptions.localData.delete.confirmation.message"))
        }
        .presentationCompactAdaptation(.sheet)
    }

    // MARK: - Account Section

    @ViewBuilder
    private var accountSection: some View {
        Section {
            if let validator, validator.hasAvailableAccounts {
                Picker(
                    String(localized: "settings.subscriptions.account.label"),
                    selection: Binding(
                        get: { currentAccount },
                        set: { newAccount in
                            if newAccount != currentAccount {
                                pendingAccountChange = newAccount
                                showingAccountSwitchConfirmation = true
                            }
                        }
                    )
                ) {
                    ForEach(validator.availableAccounts, id: \.self) { account in
                        Text(validator.displayName(for: account))
                            .tag(account)
                    }
                }
            } else {
                // No accounts available - show setup prompt
                VStack(alignment: .leading, spacing: 8) {
                    Label(
                        String(localized: "settings.subscriptions.account.noAccounts.title"),
                        systemImage: "exclamationmark.triangle"
                    )
                    .foregroundStyle(.secondary)

                    Text(String(localized: "settings.subscriptions.account.noAccounts.message"))
                        .font(.caption)
                        .foregroundStyle(.secondary)

                    Button(String(localized: "settings.subscriptions.account.noAccounts.action")) {
                        appEnvironment?.navigationCoordinator.navigate(to: .settings)
                    }
                    .font(.caption)
                    .padding(.top, 4)
                }
                .padding(.vertical, 4)
            }
        } footer: {
            if validator?.hasAvailableAccounts == true {
                Text(String(localized: "settings.subscriptions.account.footer"))
            }
        }
    }

    // MARK: - Account Switch Actions

    private func confirmAccountSwitch() {
        guard let newAccount = pendingAccountChange else { return }

        settingsManager?.subscriptionAccount = newAccount
        SubscriptionFeedCache.shared.handleAccountChange()

        // Trigger feed refresh in background
        Task {
            guard let appEnvironment else { return }
            await SubscriptionFeedCache.shared.refresh(using: appEnvironment)
        }

        pendingAccountChange = nil
        LoggingService.shared.logSubscriptions("Switched subscription account to: \(String(describing: newAccount.type))")
    }

    private func cancelAccountSwitch() {
        pendingAccountChange = nil
    }

    // MARK: - Import Section

    @ViewBuilder
    private var importSection: some View {
        Section {
            Button {
                showImportPicker()
            } label: {
                HStack {
                    Label(String(localized: "settings.subscriptions.import.button"), systemImage: "square.and.arrow.down")
                    Spacer()
                    if isImporting {
                        ProgressView()
                    }
                }
            }
            .disabled(isImporting)
        } header: {
            Text(String(localized: "settings.subscriptions.import.title"))
        } footer: {
            Text(String(localized: "settings.subscriptions.import.footer"))
        }
    }

    // MARK: - Export Section

    @ViewBuilder
    private var exportSection: some View {
        Section {
            Picker(String(localized: "settings.subscriptions.export.format"), selection: $selectedExportFormat) {
                ForEach(SubscriptionExportFormat.allCases) { format in
                    Text(format.rawValue).tag(format)
                }
            }

            Button {
                exportSubscriptions()
            } label: {
                HStack {
                    Label(String(localized: "settings.subscriptions.export.button"), systemImage: "square.and.arrow.up")
                    Spacer()
                    if isExporting {
                        ProgressView()
                    }
                }
            }
            .disabled(subscriptionCount == 0 || isExporting)
        } header: {
            Text(String(localized: "settings.subscriptions.export.title"))
        } footer: {
            Text(String(localized: "settings.subscriptions.export.footer \(subscriptionCount)"))
        }
    }

    // MARK: - Local Data Section

    /// Lets the user delete locally stored subscriptions while a server account
    /// is active. Hidden in local mode, where the account IS the local store.
    @ViewBuilder
    private var localDataSection: some View {
        if currentAccount.type != .local && localSubscriptionCount > 0 {
            Section {
                Button(role: .destructive) {
                    showingDeleteLocalConfirmation = true
                } label: {
                    Label(String(localized: "settings.subscriptions.localData.delete.button"), systemImage: "trash")
                }
            } header: {
                Text(String(localized: "settings.subscriptions.localData.title"))
            } footer: {
                Text(String(localized: "settings.subscriptions.localData.footer \(localSubscriptionCount)"))
            }
        }
    }

    // MARK: - Actions

    private func showImportPicker() {
        #if os(iOS)
        showingImportPicker = true
        #elseif os(macOS)
        showMacOSImportPanel()
        #endif
    }

    #if os(macOS)
    private func showMacOSImportPanel() {
        let panel = NSOpenPanel()
        panel.allowedContentTypes = [
            UTType.commaSeparatedText,
            UTType(filenameExtension: "opml") ?? .xml,
            UTType.xml
        ]
        panel.canChooseFiles = true
        panel.canChooseDirectories = false
        panel.allowsMultipleSelection = false
        panel.message = String(localized: "settings.subscriptions.import.panel.message")

        if panel.runModal() == .OK, let url = panel.url {
            handleImportedFile(url)
        }
    }
    #endif

    private func handleImportedFile(_ url: URL) {
        isImporting = true

        Task {
            do {
                // Read file data
                let data: Data
                if url.startAccessingSecurityScopedResource() {
                    defer { url.stopAccessingSecurityScopedResource() }
                    data = try Data(contentsOf: url)
                } else {
                    data = try Data(contentsOf: url)
                }

                // Parse subscriptions
                let parseResult = try SubscriptionImportExport.parseAuto(data)

                // Import into the active account (local store or server)
                guard let subscriptionService else {
                    throw SubscriptionImportError.invalidData
                }

                let importStats = await subscriptionService.importSubscriptions(parseResult.channels)

                await MainActor.run {
                    isImporting = false
                    importResult = (imported: importStats.imported, skipped: importStats.skipped, format: parseResult.format)
                    showingImportResult = true
                    LoggingService.shared.logSubscriptions("Import completed: \(importStats.imported) imported, \(importStats.skipped) skipped")
                }
                await refreshAccountSubscriptionCount()
            } catch {
                await MainActor.run {
                    isImporting = false
                    importError = error.localizedDescription
                    showingImportError = true
                    LoggingService.shared.logSubscriptionsError("Import failed", error: error)
                }
            }
        }
    }

    private func exportSubscriptions() {
        isExporting = true
        Task {
            await performExport()
            isExporting = false
        }
    }

    /// Exports subscriptions of the active account: the local store in local
    /// mode, or the server's list for Invidious/Piped accounts.
    private func performExport() async {
        let subscriptions: [Subscription]
        if currentAccount.type == .local {
            guard let dataManager else { return }
            subscriptions = dataManager.allSubscriptions
        } else {
            guard let subscriptionService else { return }
            do {
                let channels = try await subscriptionService.fetchSubscriptions()
                accountSubscriptionCount = channels.count
                // Detached model instances used purely as export carriers
                subscriptions = channels.map { Subscription.from(channel: $0) }
            } catch {
                LoggingService.shared.logSubscriptionsError("Failed to fetch account subscriptions for export", error: error)
                appEnvironment?.toastManager.showError(
                    String(localized: "settings.subscriptions.export.error.title"),
                    subtitle: error.localizedDescription
                )
                return
            }
        }

        let data: Data?
        switch selectedExportFormat {
        case .json:
            data = SubscriptionImportExport.exportToJSON(subscriptions)
        case .opml:
            data = SubscriptionImportExport.exportToOPML(subscriptions)
        }

        guard let exportData = data else {
            LoggingService.shared.logSubscriptionsError("Failed to generate export data")
            return
        }

        let filename = SubscriptionImportExport.generateExportFilename(format: selectedExportFormat)

        #if os(iOS)
        // Write to temp file and share
        let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent(filename)
        do {
            try exportData.write(to: tempURL)
            exportFile = ExportFile(url: tempURL)
        } catch {
            LoggingService.shared.logSubscriptionsError("Failed to write export file", error: error)
        }
        #elseif os(macOS)
        showMacOSSavePanel(data: exportData, filename: filename)
        #endif
    }

    /// Refreshes the account-scoped subscription count shown in the export footer.
    private func refreshAccountSubscriptionCount() async {
        guard let subscriptionService else { return }

        if let cached = subscriptionService.cachedSubscriptionCount {
            accountSubscriptionCount = cached
        }
        if subscriptionService.currentAccountType != .local {
            if let channels = try? await subscriptionService.fetchSubscriptions() {
                accountSubscriptionCount = channels.count
            }
        }
    }

    private func deleteLocalSubscriptions() {
        dataManager?.deleteAllSubscriptions()
        appEnvironment?.toastManager.showSuccess(String(localized: "settings.subscriptions.localData.deleted.title"))
        Task {
            await refreshAccountSubscriptionCount()
        }
    }

    #if os(macOS)
    private func showMacOSSavePanel(data: Data, filename: String) {
        let panel = NSSavePanel()
        panel.nameFieldStringValue = filename
        panel.allowedContentTypes = selectedExportFormat == .json ? [.json] : [UTType(filenameExtension: "opml") ?? .xml]

        if panel.runModal() == .OK, let url = panel.url {
            do {
                try data.write(to: url)
                LoggingService.shared.logSubscriptions("Exported subscriptions to \(url.path)")
            } catch {
                LoggingService.shared.logSubscriptionsError("Failed to save export", error: error)
            }
        }
    }
    #endif
}

// MARK: - File Picker (iOS)

#if os(iOS)
struct SubscriptionFilePickerView: UIViewControllerRepresentable {
    let onSelect: (URL) -> Void

    func makeUIViewController(context: Context) -> UIDocumentPickerViewController {
        let supportedTypes: [UTType] = [
            .commaSeparatedText,
            UTType(filenameExtension: "opml") ?? .xml,
            .xml,
            .text
        ]

        let picker = UIDocumentPickerViewController(forOpeningContentTypes: supportedTypes)
        picker.delegate = context.coordinator
        picker.allowsMultipleSelection = false
        return picker
    }

    func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: Context) {}

    func makeCoordinator() -> Coordinator {
        Coordinator(onSelect: onSelect)
    }

    class Coordinator: NSObject, UIDocumentPickerDelegate {
        let onSelect: (URL) -> Void

        init(onSelect: @escaping (URL) -> Void) {
            self.onSelect = onSelect
        }

        func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
            guard let url = urls.first else { return }
            onSelect(url)
        }
    }
}
#endif

// MARK: - Preview

#Preview {
    NavigationStack {
        SubscriptionsSettingsView()
    }
    .appEnvironment(.preview)
}
#endif
