// SPDX-FileCopyrightText: 2026 John Burwell and contributors
// SPDX-License-Identifier: AGPL-3.0-or-later

import Foundation
import SwiftUI

#if DEBUG

nonisolated enum PreviewFixtures {
    static let baseDate = Date(timeIntervalSinceReferenceDate: 770_000_000)

    static let sourceOneURL = URL(fileURLWithPath: "/tmp/preview-library-1")
    static let sourceTwoURL = URL(fileURLWithPath: "/tmp/preview-library-2")

    static let worldCollectionURL = sourceOneURL.appendingPathComponent(MinecraftContentType.world.collectionFolderName)
    static let behaviorCollectionURL = sourceOneURL.appendingPathComponent(MinecraftContentType.behaviorPack.collectionFolderName)
    static let resourceCollectionURL = sourceOneURL.appendingPathComponent(MinecraftContentType.resourcePack.collectionFolderName)

    static let behaviorPackIdentity = PackIdentity(
        type: .behaviorPack,
        uuid: "874117c4-3f9c-432e-bd64-da7336313337",
        version: "3.0.0",
        fallbackName: "Wither Storm Behavior",
        fallbackLocationHint: behaviorCollectionURL.lastPathComponent
    )

    static let resourcePackIdentity = PackIdentity(
        type: .resourcePack,
        uuid: "823cbe13-eac7-49a4-b9cd-1dd25ab1048a",
        version: "3.0.0",
        fallbackName: "Wither Storm Resources",
        fallbackLocationHint: resourceCollectionURL.lastPathComponent
    )

    static let behaviorPackReference = ContentPackReference(
        name: "§5Cracker's Wither Storm Mod",
        type: .behaviorPack,
        uuid: behaviorPackIdentity.uuid,
        version: behaviorPackIdentity.version,
        source: .referencedByWorld
    )

    static let resourcePackReference = ContentPackReference(
        name: "§5Cracker's Wither Storm Mod",
        type: .resourcePack,
        uuid: resourcePackIdentity.uuid,
        version: resourcePackIdentity.version,
        source: .referencedByWorld
    )

    static let featuredWorld = MinecraftContentItem(
        folderURL: worldCollectionURL.appendingPathComponent("world-alpha"),
        folderName: "world-alpha",
        contentType: .world,
        collectionRootURL: worldCollectionURL,
        displayName: "THE ABSOLUTELY ENORMOUS KID CHAOS LAB WITH 9999 TNT AND A SECRET LLAMA BUNKER v2.0",
        lastPlayedDate: baseDate.addingTimeInterval(-86_400 * 3),
        modifiedDate: baseDate.addingTimeInterval(-86_400 * 2),
        sizeBytes: 35_500_000,
        packReferences: [behaviorPackReference, resourcePackReference],
        metadataLoaded: true,
        sizeLoaded: true
    )

    static let siblingWorld = MinecraftContentItem(
        folderURL: worldCollectionURL.appendingPathComponent("world-beta"),
        folderName: "world-beta",
        contentType: .world,
        collectionRootURL: worldCollectionURL,
        displayName: "Sky Battle Arena",
        modifiedDate: baseDate.addingTimeInterval(-86_400 * 14),
        sizeBytes: 14_200_000,
        packReferences: [resourcePackReference],
        metadataLoaded: true,
        sizeLoaded: true
    )

    static let archiveWorld = MinecraftContentItem(
        folderURL: worldCollectionURL.appendingPathComponent("world-gamma"),
        folderName: "world-gamma",
        contentType: .world,
        collectionRootURL: worldCollectionURL,
        displayName: "Grandma's Survival Backup But Everyone Has Netherite",
        modifiedDate: baseDate.addingTimeInterval(-86_400 * 60),
        sizeBytes: 227_500_000,
        metadataLoaded: true,
        sizeLoaded: true
    )

    static let behaviorPackItem = MinecraftContentItem(
        folderURL: behaviorCollectionURL.appendingPathComponent("wither-storm-bp"),
        folderName: "wither-storm-bp",
        contentType: .behaviorPack,
        collectionRootURL: behaviorCollectionURL,
        displayName: "§5Cracker's Wither Storm Mod",
        modifiedDate: baseDate.addingTimeInterval(-86_400 * 6),
        sizeBytes: 5_300_000,
        packUUID: behaviorPackIdentity.uuid,
        packVersion: behaviorPackIdentity.version,
        metadataLoaded: true,
        sizeLoaded: true
    )

    static let resourcePackItem = MinecraftContentItem(
        folderURL: resourceCollectionURL.appendingPathComponent("wither-storm-rp"),
        folderName: "wither-storm-rp",
        contentType: .resourcePack,
        collectionRootURL: resourceCollectionURL,
        displayName: "§5Cracker's Wither Storm Mod",
        modifiedDate: baseDate.addingTimeInterval(-86_400 * 6),
        sizeBytes: 8_900_000,
        packUUID: resourcePackIdentity.uuid,
        packVersion: resourcePackIdentity.version,
        metadataLoaded: true,
        sizeLoaded: true
    )

    static let secondLibraryPack = MinecraftContentItem(
        folderURL: sourceTwoURL
            .appendingPathComponent(MinecraftContentType.resourcePack.collectionFolderName)
            .appendingPathComponent("cartoon-rp"),
        folderName: "cartoon-rp",
        contentType: .resourcePack,
        collectionRootURL: sourceTwoURL.appendingPathComponent(MinecraftContentType.resourcePack.collectionFolderName),
        displayName: "Cartoon Blocks Remix",
        modifiedDate: baseDate.addingTimeInterval(-86_400 * 11),
        sizeBytes: 12_700_000,
        packUUID: "246df391-7dd1-4df6-b7aa-308a94a85d81",
        packVersion: "1.4.2",
        metadataLoaded: true,
        sizeLoaded: true
    )

    static let primarySource: MinecraftSource = {
        var source = MinecraftSource(folderURL: sourceOneURL, availability: .available)
        source.displayName = "Kid iPad Imports"
        source.displayItems = [
            featuredWorld,
            siblingWorld,
            archiveWorld,
            behaviorPackItem,
            resourcePackItem
        ]
        source.displayItemCountsByType = source.displayItems.reduce(into: [MinecraftContentType: Int]()) { counts, item in
            counts[item.contentType, default: 0] += 1
        }
        source.displayItemCountsByKind = source.displayItems.reduce(into: [MinecraftContentKind: Int]()) { counts, item in
            counts[item.contentKind, default: 0] += 1
        }
        source.rawItems = source.displayItems
        source.logicalPacks = [
            LogicalPack(
                id: behaviorPackIdentity,
                contentType: .behaviorPack,
                displayName: behaviorPackItem.displayName,
                uuid: behaviorPackItem.packUUID,
                version: behaviorPackItem.packVersion,
                representativeItemID: behaviorPackItem.id,
                instanceItemIDs: [behaviorPackItem.id],
                isSuspicious: false
            ),
            LogicalPack(
                id: resourcePackIdentity,
                contentType: .resourcePack,
                displayName: resourcePackItem.displayName,
                uuid: resourcePackItem.packUUID,
                version: resourcePackItem.packVersion,
                representativeItemID: resourcePackItem.id,
                instanceItemIDs: [resourcePackItem.id],
                isSuspicious: false
            )
        ]
        source.logicalWorlds = [
            LogicalWorld(
                id: featuredWorld.id,
                itemID: featuredWorld.id,
                usedPackIDs: [behaviorPackIdentity, resourcePackIdentity],
                unresolvedReferences: []
            ),
            LogicalWorld(
                id: siblingWorld.id,
                itemID: siblingWorld.id,
                usedPackIDs: [resourcePackIdentity],
                unresolvedReferences: []
            )
        ]
        source.packInstances = [
            PackInstance(
                id: behaviorPackItem.id,
                itemID: behaviorPackItem.id,
                sourceID: source.id,
                logicalPackID: behaviorPackIdentity,
                origin: .foundInCollection,
                hostWorldItemID: nil
            ),
            PackInstance(
                id: resourcePackItem.id,
                itemID: resourcePackItem.id,
                sourceID: source.id,
                logicalPackID: resourcePackIdentity,
                origin: .foundInCollection,
                hostWorldItemID: nil
            )
        ]
        source.worldPackRelationships = [
            WorldPackRelationship(
                worldItemID: featuredWorld.id,
                logicalPackID: behaviorPackIdentity,
                reference: behaviorPackReference
            ),
            WorldPackRelationship(
                worldItemID: featuredWorld.id,
                logicalPackID: resourcePackIdentity,
                reference: resourcePackReference
            ),
            WorldPackRelationship(
                worldItemID: siblingWorld.id,
                logicalPackID: resourcePackIdentity,
                reference: resourcePackReference
            )
        ]
        source.indexedItemCount = source.displayItems.count
        source.indexedDetailCount = source.displayItems.count
        source.lastScanDate = baseDate
        return source
    }()

    static let secondarySource: MinecraftSource = {
        var source = MinecraftSource(folderURL: sourceTwoURL, availability: .available)
        source.displayName = "Downloads"
        source.displayItems = [secondLibraryPack]
        source.displayItemCountsByType = source.displayItems.reduce(into: [MinecraftContentType: Int]()) { counts, item in
            counts[item.contentType, default: 0] += 1
        }
        source.displayItemCountsByKind = source.displayItems.reduce(into: [MinecraftContentKind: Int]()) { counts, item in
            counts[item.contentKind, default: 0] += 1
        }
        source.rawItems = source.displayItems
        source.indexedItemCount = source.displayItems.count
        source.indexedDetailCount = source.displayItems.count
        source.lastScanDate = baseDate.addingTimeInterval(-3_600)
        return source
    }()

    static let allSources = [primarySource, secondarySource]

    static let directoryEntries = [
        DirectoryEntry(name: "db", isDirectory: true),
        DirectoryEntry(name: "level.dat", isDirectory: false),
        DirectoryEntry(name: "levelname.txt", isDirectory: false),
        DirectoryEntry(name: "world_icon.jpeg", isDirectory: false),
        DirectoryEntry(name: "resource_packs", isDirectory: true)
    ]
}

struct PreviewSourceAccess: SourceAccessMethod {
    nonisolated let accessorIdentifier: SourceAccessorIdentifier = "preview-source"

    nonisolated init() {}

    nonisolated func accessStatus(for source: MinecraftSource) async -> SourceAccessStatus {
        SourceAccessStatus(
            availability: .available,
            mode: .localFileSystem,
            displayName: source.displayName,
            iconSystemName: "folder",
            statusText: nil,
            warningText: nil
        )
    }

    nonisolated func capabilities(for source: MinecraftSource) async -> SourceCapabilities {
        _ = source
        return .localFolder
    }

    nonisolated func discoverItems(
        for source: MinecraftSource,
        mode: SourceDiscoveryMode,
        onDiscovered: @escaping @Sendable (MinecraftContentItem) -> Void
    ) async throws {
        _ = mode
        for item in source.displayItems {
            onDiscovered(item)
        }
    }

    nonisolated func listItemContents(for item: MinecraftContentItem, in source: MinecraftSource) async throws -> [DirectoryEntry] {
        _ = item
        _ = source
        return PreviewFixtures.directoryEntries
    }

    nonisolated func materializeItem(for item: MinecraftContentItem, in source: MinecraftSource) async throws -> URL {
        _ = source
        return item.folderURL
    }
}

@MainActor
extension SourceLibrary {
    static func makePreview() -> SourceLibrary {
        let library = SourceLibrary(
            sourceAccessMethod: PreviewSourceAccess(),
            restoresPersistedSources: false,
            startsBackgroundRefresh: false
        )
        library.sources = PreviewFixtures.allSources
        library.sourceCandidates = [
            SourceCandidate(
                providerID: LocalFolderSourceAccess().accessorIdentifier,
                edition: .bedrock,
                sourceRootURL: URL(fileURLWithPath: "/tmp/preview-candidate"),
                displayName: "Found Minecraft Folder",
                confidence: .strong,
                reason: "Contains Minecraft content folders",
                detectedKinds: [.world, .resourcePack]
            )
        ]
        return library
    }
}

extension ContentViewDependencies {
    @MainActor
    static func makePreview() -> ContentViewDependencies {
        let connectedDeviceAccess = AppleMobileDeviceSourceAccess()
        return ContentViewDependencies(
            library: .makePreview(),
            connectedDeviceAccess: connectedDeviceAccess,
            deviceSourceFactory: ConnectedDeviceSourceFactory(),
            itemActionService: ContentItemActionService()
        )
    }
}

@MainActor
struct SidebarColumnPreviewContainer: View {
    @State private var selection: SidebarSelection? = .allContent(sourceID: PreviewFixtures.primarySource.id)

    var body: some View {
        NavigationStack {
            SourcesSidebarView(
                sources: PreviewFixtures.allSources,
                connectedDevices: [],
                sourceCandidates: [],
                isDiscoveringSourceCandidates: false,
                selection: $selection,
                addSourceAction: {},
                discoverSourcesAction: {},
                addCandidateSourceAction: { _ in },
                addDeviceSourceAction: {},
                addConnectedDeviceAction: { _ in },
                rescanSourceAction: { _ in },
                removeSourceAction: { _ in },
                filters: { source in
                    let allFilter = SidebarFilter(
                        title: "All Content",
                        iconName: "square.stack.3d.up",
                        count: source.displayItems.count,
                        selection: .allContent(sourceID: source.id)
                    )

                    let groupedFilters = MinecraftContentType.allCases.compactMap { contentType -> SidebarFilter? in
                        let count = source.displayItems.filter { $0.contentType == contentType }.count
                        guard count > 0 else {
                            return nil
                        }

                        let iconName: String
                        switch contentType {
                        case .world:
                            iconName = "globe.europe.africa"
                        case .behaviorPack:
                            iconName = "shippingbox"
                        case .resourcePack:
                            iconName = "paintpalette"
                        case .skinPack:
                            iconName = "person.crop.square"
                        case .worldTemplate:
                            iconName = "doc.on.doc"
                        }

                        return SidebarFilter(
                            title: contentType.rawValue,
                            iconName: iconName,
                            count: count,
                            selection: .contentType(sourceID: source.id, contentType: contentType)
                        )
                    }

                    return [allFilter] + groupedFilters
                }
            )
        }
    }
}

struct ItemListColumnPreviewContainer: View {
    @State private var isDropTargeted = false
    @State private var selectedItemID: MinecraftContentItem.ID? = PreviewFixtures.featuredWorld.id
    @State private var searchText = ""
    @State private var sortMode: ItemSortMode = .modifiedDate

    var body: some View {
        NavigationStack {
            ItemListColumnView(
                isEmpty: false,
                isDropTargeted: $isDropTargeted,
                selectedItemID: $selectedItemID,
                searchText: $searchText,
                sortMode: $sortMode,
                showsHeader: true,
                sourceName: PreviewFixtures.primarySource.displayName,
                showsSourceName: false,
                title: "All Items",
                subtitle: "5 items in Kid iPad Imports",
                showsSubtitle: false,
                isRefreshing: false,
                showsProjectionLoadingState: false,
                items: PreviewFixtures.primarySource.displayItems,
                searchPrompt: "Search Worlds",
                chooseFolderAction: {},
                dropAction: { _ in false },
                itemContextMenu: { item in
                    Button("Reveal \(item.displayName)") {}
                }
            )
        }
    }
}

struct ItemDetailColumnPreviewContainer: View {
    var body: some View {
        NavigationStack {
            ItemDetailColumnView(
                item: PreviewFixtures.featuredWorld,
                source: PreviewFixtures.primarySource,
                sourceCandidate: nil,
                connectedDevice: nil,
                showsSourceDetails: false,
                behaviorPacks: PreviewFixtures.primarySource.resolvedPackReferences(for: PreviewFixtures.featuredWorld.id, type: .behaviorPack),
                resourcePacks: PreviewFixtures.primarySource.resolvedPackReferences(for: PreviewFixtures.featuredWorld.id, type: .resourcePack),
                worldsUsingPack: [],
                backingPackInstances: [],
                isSuspiciousPack: false,
                contents: PreviewFixtures.directoryEntries,
                directoryPreviewLimit: 12,
                isEmpty: false,
                isPerformingItemAction: false,
                areFileActionsEnabled: true,
                exportTitle: PreviewFixtures.featuredWorld.contentType.exportTitle,
                exportAction: {},
                revealAction: {},
                shareAction: { _ in },
                addCandidateSourceAction: { _ in },
                revealCandidateAction: { _ in },
                addConnectedDeviceAction: { _ in }
            )
        }
    }
}
#endif
