import SwiftUI

/// Standalone grammar notes view with its own NavigationStack (used outside GuideView).
struct GrammarNotesView: View {
    @State private var selectedNote: GrammarNote?

    var body: some View {
        NavigationStack {
            GrammarNotesListView(selectedNote: $selectedNote)
                .navigationDestination(for: GrammarNote.self) { note in
                    GrammarNoteDetailView(note: note)
                }
        }
    }
}

/// List of grammar notes that works inside a NavigationSplitView via a selection binding.
struct GrammarNotesListView: View {
    @Binding var selectedNote: GrammarNote?

    private var groupedNotes: [(String, [GrammarNote])] {
        let grouped = Dictionary(grouping: GrammarNote.allNotes, by: \.category)
        var seen: [String] = []
        for note in GrammarNote.allNotes {
            if !seen.contains(note.category) {
                seen.append(note.category)
            }
        }
        return seen.compactMap { cat in
            guard let notes = grouped[cat] else { return nil }
            return (cat, notes)
        }
    }

    var body: some View {
        List(selection: $selectedNote) {
            ForEach(groupedNotes, id: \.0) { category, notes in
                Section(category) {
                    ForEach(notes) { note in
                        NavigationLink(value: note) {
                            GrammarNoteRow(note: note)
                        }
                    }
                }
            }
        }
    }
}

// MARK: - Row

private struct GrammarNoteRow: View {
    let note: GrammarNote

    var body: some View {
        VStack(alignment: .leading, spacing: 2) {
            Text(note.title)
                .font(.headline)
            Text(note.category)
                .font(.subheadline)
                .foregroundStyle(.secondary)
        }
    }
}

// MARK: - Detail

struct GrammarNoteDetailView: View {
    let note: GrammarNote

    var body: some View {
        ScrollView {
            VStack(alignment: .leading, spacing: 20) {
                // Title header
                VStack(alignment: .leading, spacing: 6) {
                    Text(note.title)
                        .font(.title.bold())
                    Text(note.category)
                        .font(.subheadline)
                        .foregroundStyle(.secondary)
                        .padding(.horizontal, 10)
                        .padding(.vertical, 4)
                        .background(.fill.tertiary, in: Capsule())
                }

                Divider()

                // Parsed body
                FormattedGrammarBody(content: note.body)
            }
            .padding()
            .adaptiveContainer(maxWidth: 800)
        }
        .navigationTitle(note.title)
        .navigationBarTitleDisplayMode(.inline)
    }
}

// MARK: - Formatted Body

private struct FormattedGrammarBody: View {
    let content: String

    private var lines: [GrammarLine] {
        GrammarLine.parse(content)
    }

    var body: some View {
        VStack(alignment: .leading, spacing: 10) {
            ForEach(lines) { line in
                switch line.kind {
                case .paragraph(let text):
                    renderParagraph(text)
                case .spanishExample(let spanish):
                    Text(spanish)
                        .font(.body.weight(.medium))
                        .italic()
                        .padding(.leading, 12)
                case .examplePair(let spanish, let english):
                    VStack(alignment: .leading, spacing: 3) {
                        Text(spanish)
                            .font(.body.weight(.medium))
                            .italic()
                        Text(english)
                            .font(.callout)
                            .foregroundStyle(.secondary)
                    }
                    .padding(.leading, 12)
                case .heading(let text):
                    Text(text)
                        .font(.headline)
                        .padding(.top, 6)
                }
            }
        }
    }

    @ViewBuilder
    private func renderParagraph(_ text: String) -> some View {
        Text(parseInlineFormatting(text))
            .font(.body)
            .lineSpacing(4)
    }

    private func parseInlineFormatting(_ text: String) -> AttributedString {
        var result = AttributedString()
        var remaining = text[text.startIndex...]

        while !remaining.isEmpty {
            // Look for *italic* or **bold** markers
            if let starRange = remaining.range(of: "*") {
                // Add text before the star
                let before = remaining[remaining.startIndex..<starRange.lowerBound]
                if !before.isEmpty {
                    result.append(AttributedString(String(before)))
                }

                let afterStar = remaining[starRange.upperBound...]

                // Check for **bold**
                if afterStar.hasPrefix("*") {
                    let boldStart = afterStar.index(after: afterStar.startIndex)
                    if let endBold = afterStar[boldStart...].range(of: "**") {
                        let boldText = String(afterStar[boldStart..<endBold.lowerBound])
                        var attr = AttributedString(boldText)
                        attr.font = .body.bold()
                        result.append(attr)
                        remaining = afterStar[endBold.upperBound...]
                        continue
                    }
                }

                // Single *italic*
                if let endItalic = afterStar.range(of: "*") {
                    let italicText = String(afterStar[afterStar.startIndex..<endItalic.lowerBound])
                    var attr = AttributedString(italicText)
                    attr.font = .body.italic()
                    result.append(attr)
                    remaining = afterStar[endItalic.upperBound...]
                    continue
                }

                // No closing star found, just add the star literally
                result.append(AttributedString("*"))
                remaining = afterStar
            } else {
                // No more stars, add the rest
                result.append(AttributedString(String(remaining)))
                break
            }
        }

        return result
    }
}

// MARK: - Grammar Line Parsing

private struct GrammarLine: Identifiable {
    let id: Int
    let kind: Kind

    enum Kind {
        case paragraph(String)
        case heading(String)
        case spanishExample(String)
        case examplePair(spanish: String, english: String)
    }

    static func parse(_ body: String) -> [GrammarLine] {
        let rawLines = body.components(separatedBy: "\n")
        var result: [GrammarLine] = []
        var index = 0
        var paragraphBuffer = ""

        func flushParagraph() {
            let trimmed = paragraphBuffer.trimmingCharacters(in: .whitespacesAndNewlines)
            if !trimmed.isEmpty {
                result.append(GrammarLine(id: result.count, kind: .paragraph(trimmed)))
            }
            paragraphBuffer = ""
        }

        while index < rawLines.count {
            let line = rawLines[index].trimmingCharacters(in: .whitespaces)

            // Empty line: flush paragraph
            if line.isEmpty {
                flushParagraph()
                index += 1
                continue
            }

            // Heading: **text** on its own line that doesn't contain example patterns
            if line.hasPrefix("**") && line.hasSuffix("**") && !line.contains("*—*") && !line.contains(" — ") {
                flushParagraph()
                let heading = line
                    .replacingOccurrences(of: "**", with: "")
                    .trimmingCharacters(in: .whitespaces)
                result.append(GrammarLine(id: result.count, kind: .heading(heading)))
                index += 1
                continue
            }

            // Example line: starts with * and contains Spanish (italic example)
            // Pattern: *Spanish sentence.* — English translation.
            if line.hasPrefix("*") && !line.hasPrefix("**") {
                flushParagraph()

                // Check for pattern: *spanish* — english
                if let dashRange = line.range(of: " — ") ?? line.range(of: " -- ") {
                    let spanishPart = String(line[line.startIndex..<dashRange.lowerBound])
                        .replacingOccurrences(of: "*", with: "")
                        .trimmingCharacters(in: .whitespaces)
                    let englishPart = String(line[dashRange.upperBound...])
                        .replacingOccurrences(of: "*", with: "")
                        .trimmingCharacters(in: .whitespaces)
                    result.append(GrammarLine(id: result.count, kind: .examplePair(spanish: spanishPart, english: englishPart)))
                } else {
                    // Just a Spanish example without translation
                    let spanish = line.replacingOccurrences(of: "*", with: "")
                        .trimmingCharacters(in: .whitespaces)
                    result.append(GrammarLine(id: result.count, kind: .spanishExample(spanish)))
                }
                index += 1
                continue
            }

            // Regular text: accumulate into paragraph
            if !paragraphBuffer.isEmpty {
                paragraphBuffer += " "
            }
            paragraphBuffer += line
            index += 1
        }

        flushParagraph()
        return result
    }
}

// MARK: - Hashable/Equatable conformance for NavigationLink

extension GrammarNote: Hashable, Equatable {
    static func == (lhs: GrammarNote, rhs: GrammarNote) -> Bool {
        lhs.id == rhs.id
    }

    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }
}

#Preview {
    NavigationStack {
        GrammarNotesView()
    }
}
