import SwiftUI

struct NormalGeneratorView: View {
    @ObservedObject var store: NormalGeneratorStore

    var body: some View {
        HSplitView {
            NormalInputPanel(store: store)
                .frame(minWidth: 290, idealWidth: 320, maxWidth: 380)

            NormalSettingsPanel(store: store)
                .frame(minWidth: 360, idealWidth: 440)

            NormalPreviewPanel(store: store)
                .frame(minWidth: 320, idealWidth: 380)
        }
    }
}

private struct NormalInputPanel: View {
    @ObservedObject var store: NormalGeneratorStore
    @State private var isDropTarget = false

    var body: some View {
        VStack(alignment: .leading, spacing: 0) {
            HStack {
                Text("Map Source")
                    .font(.title3.weight(.semibold))
                Spacer()
                Button {
                    store.reset()
                } label: {
                    Image(systemName: "trash")
                }
                .buttonStyle(.borderless)
                .disabled(store.source == nil)
                .help("Reset Input")
            }
            .padding([.horizontal, .top])
            .padding(.bottom, 8)

            VStack(alignment: .leading, spacing: 8) {
                HStack(spacing: 8) {
                    Image(systemName: "photo")
                        .foregroundStyle(.secondary)
                        .frame(width: 18)

                    Text("Texture")
                        .font(.headline)
                        .lineLimit(1)

                    Text("Required")
                        .font(.caption2)
                        .foregroundStyle(.secondary)
                        .padding(.horizontal, 6)
                        .padding(.vertical, 2)
                        .background(.tertiary.opacity(0.35), in: Capsule())

                    Spacer()

                    if store.source != nil {
                        Button {
                            store.removeSourceTexture()
                        } label: {
                            Image(systemName: "xmark.circle")
                        }
                        .buttonStyle(.borderless)
                        .help("Remove Source")
                    }
                }

                if let source = store.source {
                    HStack(spacing: 10) {
                        Image(nsImage: source.preview)
                            .resizable()
                            .aspectRatio(contentMode: .fill)
                            .frame(width: 54, height: 54)
                            .clipShape(RoundedRectangle(cornerRadius: 6))

                        VStack(alignment: .leading, spacing: 3) {
                            Text(source.fileName)
                                .font(.callout)
                                .lineLimit(1)
                            Text(source.sizeLabel)
                                .font(.caption)
                                .foregroundStyle(.secondary)
                        }

                        Spacer()

                        Button {
                            store.chooseSourceTexture()
                        } label: {
                            Image(systemName: "arrow.triangle.2.circlepath")
                        }
                        .buttonStyle(.borderless)
                        .help("Replace Source")
                    }
                } else {
                    Button {
                        store.chooseSourceTexture()
                    } label: {
                        Label("Choose", systemImage: "plus")
                            .frame(maxWidth: .infinity)
                    }
                    .buttonStyle(.bordered)
                }
            }
            .padding(10)
            .background(isDropTarget ? Color.accentColor.opacity(0.18) : Color.secondary.opacity(0.08), in: RoundedRectangle(cornerRadius: 8))
            .overlay(
                RoundedRectangle(cornerRadius: 8)
                    .stroke(isDropTarget ? Color.accentColor : Color.secondary.opacity(0.14), lineWidth: 1)
            )
            .padding(.horizontal)
            .help("The app estimates height, roughness, gloss, metallic and AO from one source texture.")
            .onDrop(of: [.fileURL], isTargeted: $isDropTarget) { providers in
                store.acceptDrop(providers)
            }

            Spacer()
        }
    }
}

private struct NormalSettingsPanel: View {
    @ObservedObject var store: NormalGeneratorStore

    var body: some View {
        ScrollView {
            VStack(alignment: .leading, spacing: 16) {
                Text("Generated Maps")
                    .font(.title3.weight(.semibold))

                GroupBox {
                    VStack(alignment: .leading, spacing: 12) {
                        TextField("Name", text: $store.settings.materialName)
                            .textFieldStyle(.roundedBorder)

                        Picker("Output Size", selection: $store.settings.outputResolution) {
                            ForEach(OutputResolution.allCases) { resolution in
                                Text(resolution.title).tag(resolution)
                            }
                        }
                        .pickerStyle(.segmented)
                    }
                    .padding(.vertical, 4)
                }

                GroupBox("Normal / Height") {
                    VStack(alignment: .leading, spacing: 14) {
                        NormalStrengthRow(value: $store.settings.strength)

                        Stepper(value: $store.settings.smoothing, in: 0...4) {
                            HStack {
                                Text("Smoothing")
                                Spacer()
                                Text("\(store.settings.smoothing)")
                                    .font(.callout.monospacedDigit())
                                    .foregroundStyle(.secondary)
                            }
                        }

                        Toggle("Invert height", isOn: $store.settings.invertHeight)
                        Toggle("Flip green channel", isOn: $store.settings.flipGreen)
                    }
                    .padding(.vertical, 4)
                }

                GroupBox("Roughness / Gloss") {
                    VStack(alignment: .leading, spacing: 14) {
                        PercentSliderRow(title: "Rough Min", value: $store.settings.roughnessMin)
                        PercentSliderRow(title: "Rough Max", value: $store.settings.roughnessMax)
                    }
                    .padding(.vertical, 4)
                }

                GroupBox("Metal / AO") {
                    VStack(alignment: .leading, spacing: 14) {
                        PercentSliderRow(title: "Metal", value: $store.settings.metallicStrength)
                        PercentSliderRow(title: "AO", value: $store.settings.aoStrength)
                    }
                    .padding(.vertical, 4)
                }

                GroupBox("Files") {
                    VStack(alignment: .leading, spacing: 12) {
                        Toggle("Write height helper", isOn: $store.settings.exportHeight)
                        Toggle("Write roughness", isOn: $store.settings.exportRoughness)
                        Toggle("Write gloss", isOn: $store.settings.exportGloss)
                        Toggle("Write metallic", isOn: $store.settings.exportMetallic)
                        Toggle("Write AO", isOn: $store.settings.exportAO)
                        Toggle("Write ORM", isOn: $store.settings.exportORM)
                        Toggle("Write .gltf material", isOn: $store.settings.exportGLTF)
                        Toggle("Write notes", isOn: $store.settings.exportNotes)

                        HStack {
                            Button {
                                store.buildPreview()
                            } label: {
                                Label("Preview", systemImage: "eye")
                            }
                            .disabled(!store.canExport)

                            Button {
                                store.exportSet()
                            } label: {
                                Label("Export", systemImage: "square.and.arrow.up")
                            }
                            .buttonStyle(.borderedProminent)
                            .disabled(!store.canExport)
                        }
                    }
                    .padding(.vertical, 4)
                }

                Spacer(minLength: 0)
            }
            .padding()
        }
    }
}

private struct NormalStrengthRow: View {
    @Binding var value: Double

    var body: some View {
        Grid(alignment: .leadingFirstTextBaseline, horizontalSpacing: 10, verticalSpacing: 8) {
            GridRow {
                Text("Strength")
                    .frame(width: 76, alignment: .leading)
                Slider(value: $value, in: 0.2...10.0)
                Text(String(format: "%.1f", value))
                    .font(.callout.monospacedDigit())
                    .foregroundStyle(.secondary)
                    .frame(width: 44, alignment: .trailing)
            }
        }
    }
}

private struct PercentSliderRow: View {
    let title: String
    @Binding var value: Double

    var body: some View {
        Grid(alignment: .leadingFirstTextBaseline, horizontalSpacing: 10, verticalSpacing: 8) {
            GridRow {
                Text(title)
                    .frame(width: 76, alignment: .leading)
                Slider(value: $value, in: 0...1)
                Text(Formatters.percentLabel(value))
                    .font(.callout.monospacedDigit())
                    .foregroundStyle(.secondary)
                    .frame(width: 44, alignment: .trailing)
            }
        }
    }
}

private struct NormalPreviewPanel: View {
    @ObservedObject var store: NormalGeneratorStore

    var body: some View {
        VStack(alignment: .leading, spacing: 0) {
            HStack {
                Text("Preview")
                    .font(.title3.weight(.semibold))
                Spacer()
                if let lastExportURL = store.lastExportURL {
                    Text(lastExportURL.lastPathComponent)
                        .font(.caption)
                        .foregroundStyle(.secondary)
                        .lineLimit(1)
                }
            }
            .padding([.horizontal, .top])
            .padding(.bottom, 8)

            ScrollView {
                VStack(alignment: .leading, spacing: 14) {
                    if store.previews.isEmpty {
                        NormalEmptyPreviewView()
                    } else {
                        LazyVGrid(columns: [GridItem(.adaptive(minimum: 140), spacing: 12)], spacing: 12) {
                            ForEach(NormalPreviewKind.allCases) { kind in
                                if let image = store.previews[kind] {
                                    NormalPreviewTile(kind: kind, image: image)
                                }
                            }
                        }
                    }

                    Divider()

                    Text("Log")
                        .font(.headline)

                    VStack(alignment: .leading, spacing: 6) {
                        ForEach(Array(store.logLines.enumerated()), id: \.offset) { _, line in
                            Text(line)
                                .font(.caption.monospaced())
                                .foregroundStyle(.secondary)
                                .textSelection(.enabled)
                                .frame(maxWidth: .infinity, alignment: .leading)
                        }
                    }
                }
                .padding(.horizontal)
                .padding(.bottom)
            }
        }
    }
}

private struct NormalPreviewTile: View {
    let kind: NormalPreviewKind
    let image: NSImage

    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            Image(nsImage: image)
                .resizable()
                .aspectRatio(1, contentMode: .fill)
                .frame(maxWidth: .infinity)
                .clipShape(RoundedRectangle(cornerRadius: 8))
                .overlay(
                    RoundedRectangle(cornerRadius: 8)
                        .stroke(Color.secondary.opacity(0.18), lineWidth: 1)
                )

            VStack(alignment: .leading, spacing: 2) {
                Text(kind.title)
                    .font(.headline)
                    .lineLimit(1)
                Text(kind.subtitle)
                    .font(.caption)
                    .foregroundStyle(.secondary)
                    .lineLimit(1)
            }
        }
        .padding(8)
        .background(Color.secondary.opacity(0.08), in: RoundedRectangle(cornerRadius: 8))
    }
}

private struct NormalEmptyPreviewView: View {
    var body: some View {
        VStack(spacing: 10) {
            Image(systemName: "mountain.2")
                .font(.system(size: 38))
                .foregroundStyle(.secondary)
            Text("No Preview")
                .font(.headline)
                .foregroundStyle(.secondary)
        }
        .frame(maxWidth: .infinity, minHeight: 180)
        .background(Color.secondary.opacity(0.08), in: RoundedRectangle(cornerRadius: 8))
    }
}
