//
//  SystemBillOfMaterialsView.swift
//  Cable
//
//  Created by Stefan Lange-Hegermann on 09.10.25.
//


import SwiftUI
import SwiftData

struct SystemBillOfMaterialsView: View {
    let systemName: String
    let loads: [SavedLoad]
    let unitSystem: UnitSystem

    @Environment(\.dismiss) private var dismiss
    @Environment(\.openURL) private var openURL
    @State private var completedItemIDs: Set<String>
    @State private var suppressRowTapForID: String?

    private struct Item: Identifiable {
        enum Destination {
            case affiliate(URL)
            case amazonSearch(String)
        }

        let id: String
        let logicalID: String
        let title: String
        let detail: String
        let iconSystemName: String
        let destination: Destination
        let isPrimaryComponent: Bool
    }

    init(systemName: String, loads: [SavedLoad], unitSystem: UnitSystem) {
        self.systemName = systemName
        self.loads = loads
        self.unitSystem = unitSystem
        let initialKeys = loads.flatMap { load in
            load.bomCompletedItemIDs.map { SystemBillOfMaterialsView.storageKey(for: load, itemID: $0) }
        }
        _completedItemIDs = State(initialValue: Set(initialKeys))
        _suppressRowTapForID = State(initialValue: nil)
    }

    var body: some View {
        NavigationStack {
            List {
                if sortedLoads.isEmpty {
                    Section("Components") {
                        Text("No loads saved in this system yet.")
                            .font(.footnote)
                            .foregroundColor(.secondary)
                    }
                } else {
                    ForEach(sortedLoads) { load in
                        Section(header: sectionHeader(for: load)) {
                            ForEach(items(for: load)) { item in
                                let isCompleted = completedItemIDs.contains(item.id)
                                let destinationURL = destinationURL(for: item.destination, load: load)

                                HStack(spacing: 12) {
                                    let accessibilityLabel: String = {
                                        if isCompleted {
                                            let format = NSLocalizedString(
                                                "bom.accessibility.mark.incomplete",
                                                comment: "Accessibility label instructing VoiceOver to mark an item incomplete"
                                            )
                                            return String.localizedStringWithFormat(format, item.title)
                                        } else {
                                            let format = NSLocalizedString(
                                                "bom.accessibility.mark.complete",
                                                comment: "Accessibility label instructing VoiceOver to mark an item complete"
                                            )
                                            return String.localizedStringWithFormat(format, item.title)
                                        }
                                    }()

                                    Image(systemName: isCompleted ? "checkmark.circle.fill" : "circle")
                                        .foregroundColor(isCompleted ? .accentColor : .secondary)
                                        .imageScale(.large)
                                        .onTapGesture {
                                            setCompletion(!isCompleted, for: load, item: item)
                                            suppressRowTapForID = item.id
                                        }
                                        .accessibilityLabel(accessibilityLabel)

                                    VStack(alignment: .leading, spacing: 4) {
                                        Text(item.title)
                                            .font(.headline)
                                            .foregroundStyle(isCompleted ? Color.primary.opacity(0.7) : Color.primary)
                                            .strikethrough(isCompleted, color: .accentColor.opacity(0.6))

                                        if item.isPrimaryComponent {
                                            Text(String(localized: "component.fallback.name", comment: "Tag label marking an item as the component itself"))
                                                .font(.caption2.weight(.medium))
                                                .foregroundColor(.accentColor)
                                                .padding(.horizontal, 6)
                                                .padding(.vertical, 2)
                                                .background(Color.accentColor.opacity(0.15), in: Capsule())
                                        }

                                        Text(item.detail)
                                            .font(.subheadline)
                                            .foregroundColor(.secondary)
                                    }

                                    Spacer(minLength: 8)

                                    if destinationURL != nil {
                                        Image(systemName: "arrow.up.right")
                                            .font(.footnote.weight(.semibold))
                                            .foregroundColor(.secondary)
                                    }
                                }
                                .padding(.vertical, 10)
                                .contentShape(Rectangle())
                                .listRowInsets(EdgeInsets(top: 6, leading: 20, bottom: 6, trailing: 16))
                                .listRowBackground(
                                    Color(.secondarySystemGroupedBackground)
                                )
                                .onTapGesture {
                                    if suppressRowTapForID == item.id {
                                        suppressRowTapForID = nil
                                        return
                                    }
                                    if let destinationURL {
                                        openURL(destinationURL)
                                    }
                                    setCompletion(true, for: load, item: item)
                                    suppressRowTapForID = nil
                                    suppressRowTapForID = nil
                                }
                            }
                        }
                    }
                }

                Section {
                    Text(footerMessage)
                        .font(.footnote)
                        .foregroundColor(.secondary)
                        .padding(.vertical, 4)
                }
            }
            .listStyle(.insetGrouped)
            .navigationTitle(
                String(
                    format: NSLocalizedString(
                        "bom.navigation.title.system",
                        comment: "Navigation title for the bill of materials view"
                    ),
                    locale: Locale.current,
                    systemName
                )
            )
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .cancellationAction) {
                    Button("Close") {
                        dismiss()
                    }
                }
            }
            .onAppear {
                refreshCompletedItems()
                suppressRowTapForID = nil
            }
        }
        .accessibilityIdentifier("system-bom-view")
    }

    private var sortedLoads: [SavedLoad] {
        loads.sorted { lhs, rhs in
            lhs.name.localizedCaseInsensitiveCompare(rhs.name) == .orderedAscending
        }
    }

    private func sectionHeader(for load: SavedLoad) -> some View {
        VStack(alignment: .leading, spacing: 2) {
            let fallbackTitle = String(localized: "component.fallback.name", comment: "Fallback title for a component that lacks a name")
            Text(load.name.isEmpty ? fallbackTitle : load.name)
                .font(.headline)
            Text(dateFormatter.string(from: load.timestamp))
                .font(.caption)
                .foregroundColor(.secondary)
        }
    }

    private func items(for load: SavedLoad) -> [Item] {
        let lengthValue: Double
        if unitSystem == .imperial {
            lengthValue = load.length * 3.28084
        } else {
            lengthValue = load.length
        }
        let lengthLabel = String(format: "%.1f %@", lengthValue, unitSystem.lengthUnit)

        let crossSectionLabel: String
        let gaugeQuery: String
        let unknownSizeLabel = String(localized: "bom.size.unknown", comment: "Fallback label when the cable size is not yet determined")

        if unitSystem == .imperial {
            let awg = awgFromCrossSection(load.crossSection)
            if awg > 0 {
                crossSectionLabel = String(format: "AWG %.0f", awg)
                gaugeQuery = String(format: "AWG %.0f", awg)
            } else {
                crossSectionLabel = unknownSizeLabel
                gaugeQuery = "battery cable"
            }
        } else {
            if load.crossSection > 0 {
                crossSectionLabel = String(format: "%.1f mm²", load.crossSection)
                gaugeQuery = String(format: "%.1f mm2", load.crossSection)
            } else {
                crossSectionLabel = unknownSizeLabel
                gaugeQuery = "battery cable"
            }
        }

        let cableDetail = "\(lengthLabel) • \(crossSectionLabel)"

        let calculatedPower = load.power > 0 ? load.power : load.voltage * load.current
        let powerDetail = String(format: "%.0f W @ %.1f V", calculatedPower, load.voltage)

        let fuseRating = recommendedFuse(for: load)
        let fuseDetailFormat = NSLocalizedString(
            "bom.fuse.detail",
            comment: "Description for the fuse item in the BOM list"
        )
        let fuseDetail = String.localizedStringWithFormat(fuseDetailFormat, fuseRating)

        let cableShoesDetailFormat = NSLocalizedString(
            "bom.terminals.detail",
            comment: "Description for the cable terminals item in the BOM list"
        )
        let cableShoesDetail = String.localizedStringWithFormat(cableShoesDetailFormat, crossSectionLabel.lowercased())

        let affiliateURL = load.affiliateURLString.flatMap { URL(string: $0) }
        let deviceQuery = load.name.isEmpty
            ? String(format: "DC device %.0fW %.0fV", calculatedPower, load.voltage)
            : load.name

        let redCableQuery = "\(gaugeQuery) red battery cable"
        let blackCableQuery = "\(gaugeQuery) black battery cable"
        let fuseQuery = "inline fuse holder \(fuseRating)A"
        let terminalQuery = "\(gaugeQuery) cable shoes"

        let items: [Item] = [
            Item(
                id: Self.storageKey(for: load, itemID: "component"),
                logicalID: "component",
                title: load.name.isEmpty ? String(localized: "component.fallback.name", comment: "Fallback name for a component when no name is provided") : load.name,
                detail: powerDetail,
                iconSystemName: "bolt.fill",
                destination: affiliateURL.map { .affiliate($0) } ?? .amazonSearch(deviceQuery),
                isPrimaryComponent: true
            ),
            Item(
                id: Self.storageKey(for: load, itemID: "cable-red"),
                logicalID: "cable-red",
                title: String(localized: "bom.item.cable.red", comment: "Title for the red power cable item"),
                detail: cableDetail,
                iconSystemName: "bolt.horizontal.circle",
                destination: .amazonSearch(redCableQuery),
                isPrimaryComponent: false
            ),
            Item(
                id: Self.storageKey(for: load, itemID: "cable-black"),
                logicalID: "cable-black",
                title: String(localized: "bom.item.cable.black", comment: "Title for the black power cable item"),
                detail: cableDetail,
                iconSystemName: "bolt.horizontal.circle",
                destination: .amazonSearch(blackCableQuery),
                isPrimaryComponent: false
            ),
            Item(
                id: Self.storageKey(for: load, itemID: "fuse"),
                logicalID: "fuse",
                title: String(localized: "bom.item.fuse", comment: "Title for the fuse and holder item"),
                detail: fuseDetail,
                iconSystemName: "bolt.shield",
                destination: .amazonSearch(fuseQuery),
                isPrimaryComponent: false
            ),
            Item(
                id: Self.storageKey(for: load, itemID: "terminals"),
                logicalID: "terminals",
                title: String(localized: "bom.item.terminals", comment: "Title for the cable terminals item"),
                detail: cableShoesDetail,
                iconSystemName: "wrench.and.screwdriver",
                destination: .amazonSearch(terminalQuery),
                isPrimaryComponent: false
            )
        ]

        return items
    }

    private func destinationURL(for destination: Item.Destination, load: SavedLoad) -> URL? {
        switch destination {
        case .affiliate(let url):
            return url
        case .amazonSearch(let query):
            let countryCode = load.affiliateCountryCode ?? Locale.current.region?.identifier
            return AmazonAffiliate.searchURL(query: query, countryCode: countryCode)
        }
    }

    private static func storageKey(for load: SavedLoad, itemID: String) -> String {
        if load.identifier.isEmpty {
            load.identifier = UUID().uuidString
        }
        return "\(load.identifier)::\(itemID)"
    }

    private func setCompletion(_ isCompleted: Bool, for load: SavedLoad, item: Item) {
        if isCompleted {
            completedItemIDs.insert(item.id)
        } else {
            completedItemIDs.remove(item.id)
        }

        if load.identifier.isEmpty {
            load.identifier = UUID().uuidString
        }

        var stored = Set(load.bomCompletedItemIDs)
        if isCompleted {
            stored.insert(item.logicalID)
        } else {
            stored.remove(item.logicalID)
        }
        load.bomCompletedItemIDs = Array(stored).sorted()
    }

    private func refreshCompletedItems() {
        let keys = loads.flatMap { load in
            load.bomCompletedItemIDs.map { Self.storageKey(for: load, itemID: $0) }
        }
        completedItemIDs = Set(keys)
    }

    private func recommendedFuse(for load: SavedLoad) -> Int {
        let targetFuse = load.current * 1.25
        let standardFuses = [1, 2, 3, 5, 7, 10, 15, 20, 25, 30, 35, 40, 50, 60, 70, 80, 100, 125, 150, 175, 200, 225, 250, 300, 350, 400, 450, 500, 600, 700, 800]
        return standardFuses.first { $0 >= Int(targetFuse.rounded(.up)) } ?? standardFuses.last ?? 0
    }

    private func awgFromCrossSection(_ crossSectionMM2: Double) -> Double {
        let mapping: [(awg: Double, area: Double)] = [
            (20, 0.519), (18, 0.823), (16, 1.31), (14, 2.08), (12, 3.31), (10, 5.26),
            (8, 8.37), (6, 13.3), (4, 21.2), (2, 33.6), (1, 42.4), (0, 53.5),
            (00, 67.4), (000, 85.0), (0000, 107.0)
        ]

        guard crossSectionMM2 > 0 else { return 0 }

        let closest = mapping.min { lhs, rhs in
            abs(lhs.area - crossSectionMM2) < abs(rhs.area - crossSectionMM2)
        }

        return closest?.awg ?? 0
    }

    private var footerMessage: String {
        NSLocalizedString(
            "affiliate.disclaimer",
            comment: "Footer note reminding users that affiliate purchases may support the app"
        )
    }

    private var dateFormatter: DateFormatter {
        let formatter = DateFormatter()
        formatter.dateStyle = .medium
        formatter.timeStyle = .short
        return formatter
    }
}
