import SwiftUI

/// Edits a draft's recipe: ordered clips, trims, audio, text overlays.
/// Every change auto-saves the JSON recipe; a linear undo stack (per the plan —
/// no branching version trees) covers structural edits, and a clip-detail
/// session counts as one undoable action.
struct DraftEditorView: View {
    @EnvironmentObject private var store: DraftStore
    @EnvironmentObject private var library: SourceLibrary

    @State private var draft: Draft
    @State private var undoStack: [Draft] = []
    @State private var redoStack: [Draft] = []
    @State private var editingClip: DraftClip?
    @State private var showingClipPicker = false
    @State private var showingRename = false
    @State private var renameText = ""

    init(draft: Draft) {
        _draft = State(initialValue: draft)
    }

    var body: some View {
        List {
            if draft.clips.isEmpty {
                ContentUnavailableView(
                    "Empty draft",
                    systemImage: "film.stack",
                    description: Text("Add clips from your source library. The draft only stores references and trim points.")
                )
            } else {
                Section {
                    ForEach(Array(draft.clips.enumerated()), id: \.element.id) { index, clip in
                        Button {
                            pushUndo()
                            editingClip = clip
                        } label: {
                            clipRow(clip, number: index + 1)
                        }
                        .buttonStyle(.plain)
                    }
                    .onMove { source, destination in
                        pushUndo()
                        draft.clips.move(fromOffsets: source, toOffset: destination)
                    }
                    .onDelete { offsets in
                        pushUndo()
                        draft.clips.remove(atOffsets: offsets)
                    }
                } header: {
                    Text("Timeline · \(timeString(draft.totalDuration)) total")
                } footer: {
                    footerText
                }
            }
        }
        .navigationTitle(draft.name)
        .navigationBarTitleDisplayMode(.inline)
        .toolbar {
            ToolbarItemGroup(placement: .topBarTrailing) {
                Button {
                    undo()
                } label: {
                    Label("Undo", systemImage: "arrow.uturn.backward")
                }
                .disabled(undoStack.isEmpty)
                Button {
                    redo()
                } label: {
                    Label("Redo", systemImage: "arrow.uturn.forward")
                }
                .disabled(redoStack.isEmpty)
                Button {
                    showingClipPicker = true
                } label: {
                    Label("Add Clips", systemImage: "plus")
                }
                Menu {
                    Button {
                        renameText = draft.name
                        showingRename = true
                    } label: {
                        Label("Rename", systemImage: "pencil")
                    }
                    Button {
                        store.duplicate(draft)
                    } label: {
                        Label("Save As New Draft", systemImage: "plus.square.on.square")
                    }
                    EditButton()
                } label: {
                    Label("More", systemImage: "ellipsis.circle")
                }
            }
        }
        .onChange(of: draft) { _, newValue in
            store.save(newValue)
        }
        .sheet(isPresented: $showingClipPicker) {
            SourceClipPickerView { selected in
                pushUndo()
                for source in selected {
                    draft.clips.append(DraftClip(
                        id: UUID(),
                        sourceClipID: source.id,
                        trimStart: 0,
                        trimEnd: source.fingerprint.duration,
                        isMuted: false,
                        volume: 1.0,
                        textOverlays: []
                    ))
                }
            }
        }
        .sheet(item: $editingClip, onDismiss: {
            // A detail session with no changes shouldn't cost an undo step.
            if undoStack.last == draft {
                undoStack.removeLast()
            }
        }) { clip in
            if let index = draft.clips.firstIndex(where: { $0.id == clip.id }) {
                DraftClipDetailView(
                    clip: $draft.clips[index],
                    source: library.clips.first(where: { $0.id == clip.sourceClipID })
                )
            }
        }
        .alert("Rename Draft", isPresented: $showingRename) {
            TextField("Name", text: $renameText)
            Button("Rename") {
                let trimmed = renameText.trimmingCharacters(in: .whitespaces)
                if !trimmed.isEmpty {
                    draft.name = trimmed
                }
            }
            Button("Cancel", role: .cancel) {}
        }
    }

    // MARK: - Undo

    private func pushUndo() {
        undoStack.append(draft)
        if undoStack.count > 50 {
            undoStack.removeFirst()
        }
        redoStack.removeAll()
    }

    private func undo() {
        guard let previous = undoStack.popLast() else { return }
        redoStack.append(draft)
        draft = previous
    }

    private func redo() {
        guard let next = redoStack.popLast() else { return }
        undoStack.append(draft)
        draft = next
    }

    // MARK: - Rows

    private func clipRow(_ clip: DraftClip, number: Int) -> some View {
        let source = library.clips.first(where: { $0.id == clip.sourceClipID })
        let isMissing = isSourceMissing(clip)
        return HStack(spacing: 12) {
            Text("\(number)")
                .font(.caption.monospacedDigit())
                .foregroundStyle(.secondary)
                .frame(width: 20)
            ZStack {
                RoundedRectangle(cornerRadius: 6)
                    .fill(.quaternary)
                if let image = ThumbnailService.load(for: clip.sourceClipID) {
                    Image(uiImage: image)
                        .resizable()
                        .aspectRatio(contentMode: .fill)
                        .opacity(isMissing ? 0.4 : 1)
                } else {
                    Image(systemName: isMissing ? "questionmark.video" : "film")
                        .foregroundStyle(.secondary)
                }
            }
            .frame(width: 56, height: 56)
            .clipShape(RoundedRectangle(cornerRadius: 6))
            VStack(alignment: .leading, spacing: 3) {
                Text(source?.displayName ?? "Missing source")
                    .font(.subheadline.weight(.medium))
                    .lineLimit(1)
                Text("\(timeString(clip.trimStart)) – \(timeString(clip.trimEnd)) · \(timeString(clip.segmentDuration))")
                    .font(.caption)
                    .foregroundStyle(.secondary)
                HStack(spacing: 8) {
                    if isMissing {
                        Label("Missing — locate in Library", systemImage: "exclamationmark.triangle.fill")
                            .font(.caption2)
                            .foregroundStyle(.orange)
                    }
                    if clip.isMuted {
                        Image(systemName: "speaker.slash.fill")
                            .font(.caption2)
                            .foregroundStyle(.secondary)
                    } else if clip.volume < 1.0 {
                        Label("\(Int(clip.volume * 100))%", systemImage: "speaker.wave.1")
                            .font(.caption2)
                            .foregroundStyle(.secondary)
                    }
                    if !clip.textOverlays.isEmpty {
                        Label("\(clip.textOverlays.count)", systemImage: "textformat")
                            .font(.caption2)
                            .foregroundStyle(.secondary)
                    }
                }
            }
            Spacer()
            Image(systemName: "chevron.right")
                .font(.caption)
                .foregroundStyle(.tertiary)
        }
    }

    private var footerText: some View {
        let missingCount = draft.clips.filter(isSourceMissing).count
        return Group {
            if missingCount > 0 {
                Text("\(missingCount) clip\(missingCount == 1 ? "" : "s") missing — all edits are kept, but export stays blocked until relinked (preview/export arrive in step 3).")
            } else {
                Text("This draft is a \(recipeSizeText) recipe — no video data is stored. Preview & export arrive in step 3.")
            }
        }
    }

    private var recipeSizeText: String {
        let bytes = (try? JSONEncoder().encode(draft).count) ?? 0
        return Int64(bytes).formatted(.byteCount(style: .file))
    }

    private func isSourceMissing(_ clip: DraftClip) -> Bool {
        guard let source = library.clips.first(where: { $0.id == clip.sourceClipID }) else {
            return true
        }
        if case .missing = library.availability[source.id] ?? .unknown {
            return true
        }
        return false
    }
}
