import SwiftUI
import SwiftData
import SharedModels

/// 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.allNotesIncludingGenerated, by: \.category)
        var seen: [String] = []
        for note in GrammarNote.allNotesIncludingGenerated {
            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 onJumpToTense: ((TenseGuide) -> Void)? = nil
    @Environment(YouTubeVideoStore.self) private var videoStore
    @State private var relatedTenses: [TenseGuide] = []

    private var curatedVideo: YouTubeVideoStore.VideoEntry? {
        videoStore.video(forGrammarNoteId: note.id)
    }

    private func loadRelatedTenses() {
        guard let container = SharedStore.localContainer else {
            relatedTenses = []
            return
        }
        let context = ModelContext(container)
        let guides = ReferenceStore(context: context).fetchGuides()
        let byId = Dictionary(uniqueKeysWithValues: guides.map { ($0.tenseId, $0) })
        relatedTenses = GuideCrossLinks.tenseIds(forNote: note.id).compactMap { byId[$0] }
    }

    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())
                }

                if !relatedTenses.isEmpty {
                    relatedTensesSection
                }

                videoSection

                Divider()

                // Parsed body
                FormattedGrammarBody(content: note.body)

                // Practice button (if exercises exist for this note)
                if !GrammarExercise.exercises(for: note.id).isEmpty {
                    NavigationLink {
                        GrammarExerciseView(noteId: note.id, noteTitle: note.title)
                    } label: {
                        Label("Practice This", systemImage: "pencil.and.list.clipboard")
                            .font(.subheadline.weight(.semibold))
                            .frame(maxWidth: .infinity)
                            .padding(.vertical, 12)
                    }
                    .buttonStyle(.borderedProminent)
                    .tint(.purple)
                }
            }
            .padding()
            .adaptiveContainer(maxWidth: 800)
        }
        .navigationTitle(note.title)
        .navigationBarTitleDisplayMode(.inline)
        .onAppear(perform: loadRelatedTenses)
        .onChange(of: note.id) { _, _ in loadRelatedTenses() }
    }

    private var relatedTensesSection: some View {
        VStack(alignment: .leading, spacing: 8) {
            Label("Used in tenses", systemImage: "clock.arrow.circlepath")
                .font(.caption.weight(.semibold))
                .foregroundStyle(.secondary)
                .textCase(.uppercase)

            ScrollView(.horizontal, showsIndicators: false) {
                HStack(spacing: 8) {
                    ForEach(relatedTenses, id: \.tenseId) { guide in
                        Button {
                            onJumpToTense?(guide)
                        } label: {
                            Text(TenseInfo.find(guide.tenseId)?.english ?? guide.title)
                                .font(.caption.weight(.semibold))
                                .padding(.horizontal, 12)
                                .padding(.vertical, 7)
                                .background(.orange.opacity(0.12), in: Capsule())
                                .foregroundStyle(.orange)
                        }
                        .buttonStyle(.plain)
                    }
                }
            }
        }
    }

    @ViewBuilder
    private var videoSection: some View {
        if let video = curatedVideo {
            VideoActionsButtonRow(video: video)
        } else {
            Label("No video yet", systemImage: "play.slash")
                .font(.caption)
                .foregroundStyle(.secondary)
                .padding(.vertical, 6)
                .padding(.horizontal, 10)
                .background(.fill.quinary, in: Capsule())
        }
    }
}

// MARK: - Formatted Body

private struct FormattedGrammarBody: View {
    let content: String

    private var sections: [GrammarSection] {
        GrammarSection.group(GrammarLine.parse(content))
    }

    var body: some View {
        VStack(alignment: .leading, spacing: 16) {
            ForEach(sections) { section in
                if section.heading == nil {
                    // Intro content before the first heading — no card
                    renderLines(section.lines)
                } else {
                    // Headed section in a card
                    VStack(alignment: .leading, spacing: 10) {
                        Text(section.heading!)
                            .font(.headline)

                        renderLines(section.lines)
                    }
                    .padding()
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .background(.fill.quinary, in: RoundedRectangle(cornerRadius: 12))
                }
            }
        }
    }

    @ViewBuilder
    private func renderLines(_ lines: [GrammarLine]) -> some View {
        let blocks = ContentBlock.group(lines)
        VStack(alignment: .leading, spacing: 12) {
            ForEach(blocks) { block in
                switch block.kind {
                case .standalone(let line):
                    renderSingleLine(line)
                case .exampleCard(let header, let examples):
                    renderExampleCard(header: header, examples: examples)
                case .suffixCard(let suffix, let description, let examples):
                    renderSuffixCard(suffix: suffix, description: description, examples: examples)
                }
            }
        }
    }

    @ViewBuilder
    private func renderSingleLine(_ line: GrammarLine) -> some View {
        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, .suffixDef:
            EmptyView()
        }
    }

    @ViewBuilder
    private func renderExampleCard(header: GrammarLine?, examples: [GrammarLine]) -> some View {
        VStack(alignment: .leading, spacing: 8) {
            if let header, case .paragraph(let text) = header.kind {
                renderParagraph(text)
            }

            VStack(alignment: .leading, spacing: 6) {
                ForEach(examples) { ex in
                    if case .examplePair(let spanish, let english) = ex.kind {
                        VStack(alignment: .leading, spacing: 2) {
                            Text(spanish)
                                .font(.body.weight(.medium))
                                .italic()
                            Text(english)
                                .font(.callout)
                                .foregroundStyle(.secondary)
                        }
                    } else if case .spanishExample(let spanish) = ex.kind {
                        Text(spanish)
                            .font(.body.weight(.medium))
                            .italic()
                    }
                }
            }
        }
        .padding(12)
        .frame(maxWidth: .infinity, alignment: .leading)
        .background(.fill.quaternary, in: RoundedRectangle(cornerRadius: 10))
    }

    @ViewBuilder
    private func renderSuffixCard(suffix: String, description: String, examples: [GrammarLine]) -> some View {
        VStack(alignment: .leading, spacing: 8) {
            HStack(alignment: .firstTextBaseline, spacing: 8) {
                Text(suffix)
                    .font(.subheadline.weight(.bold).monospaced())
                    .padding(.horizontal, 10)
                    .padding(.vertical, 4)
                    .background(.tint.opacity(0.12), in: Capsule())

                Text(description)
                    .font(.callout)
                    .foregroundStyle(.secondary)
            }

            if !examples.isEmpty {
                VStack(alignment: .leading, spacing: 4) {
                    ForEach(examples) { ex in
                        if case .examplePair(let spanish, let english) = ex.kind {
                            HStack(spacing: 0) {
                                Text(spanish)
                                    .font(.body.weight(.medium))
                                    .italic()
                                Text("  ")
                                Text(english)
                                    .font(.callout)
                                    .foregroundStyle(.secondary)
                            }
                        } else if case .spanishExample(let spanish) = ex.kind {
                            Text(spanish)
                                .font(.body.weight(.medium))
                                .italic()
                        }
                    }
                }
                .padding(.leading, 8)
            }
        }
        .padding(12)
        .frame(maxWidth: .infinity, alignment: .leading)
        .background(.fill.quaternary, in: RoundedRectangle(cornerRadius: 10))
    }

    @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

    var isExample: Bool {
        switch kind {
        case .examplePair, .spanishExample: return true
        default: return false
        }
    }

    enum Kind {
        case paragraph(String)
        case heading(String)
        case spanishExample(String)
        case examplePair(spanish: String, english: String)
        /// A suffix definition line like `*-ito / -ita* — description`
        case suffixDef(suffix: String, description: 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)

                    // Detect suffix definitions: spanish part starts with "-"
                    if spanishPart.hasPrefix("-") {
                        result.append(GrammarLine(id: result.count, kind: .suffixDef(suffix: spanishPart, description: englishPart)))
                    } else {
                        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: - Content Block Grouping

/// Groups paragraphs with their trailing examples into visual cards.
/// Also handles suffix definitions as a special card type.
private struct ContentBlock: Identifiable {
    let id: Int

    enum Kind {
        /// A standalone line (paragraph with no examples, or orphaned example)
        case standalone(GrammarLine)
        /// A paragraph header followed by example pairs — rendered as a card
        case exampleCard(header: GrammarLine?, examples: [GrammarLine])
        /// A suffix definition with its examples — rendered as a pill card
        case suffixCard(suffix: String, description: String, examples: [GrammarLine])
    }

    let kind: Kind

    static func group(_ lines: [GrammarLine]) -> [ContentBlock] {
        var result: [ContentBlock] = []
        var i = 0

        while i < lines.count {
            let line = lines[i]

            // Suffix definition: collect trailing examples
            if case .suffixDef(let suffix, let desc) = line.kind {
                var examples: [GrammarLine] = []
                var j = i + 1
                while j < lines.count, lines[j].isExample {
                    examples.append(lines[j])
                    j += 1
                }
                result.append(ContentBlock(id: result.count, kind: .suffixCard(suffix: suffix, description: desc, examples: examples)))
                i = j
                continue
            }

            // Paragraph followed by examples: group into a card
            if case .paragraph = line.kind {
                var j = i + 1
                // Check if examples follow
                if j < lines.count, lines[j].isExample {
                    var examples: [GrammarLine] = []
                    while j < lines.count, lines[j].isExample {
                        examples.append(lines[j])
                        j += 1
                    }
                    result.append(ContentBlock(id: result.count, kind: .exampleCard(header: line, examples: examples)))
                    i = j
                } else {
                    // Standalone paragraph
                    result.append(ContentBlock(id: result.count, kind: .standalone(line)))
                    i += 1
                }
                continue
            }

            // Orphaned examples (no preceding paragraph) — group into a card
            if line.isExample {
                var examples: [GrammarLine] = []
                var j = i
                while j < lines.count, lines[j].isExample {
                    examples.append(lines[j])
                    j += 1
                }
                result.append(ContentBlock(id: result.count, kind: .exampleCard(header: nil, examples: examples)))
                i = j
                continue
            }

            // Skip headings (handled at section level)
            if case .heading = line.kind {
                i += 1
                continue
            }

            result.append(ContentBlock(id: result.count, kind: .standalone(line)))
            i += 1
        }
        return result
    }
}

// MARK: - Grammar Section Grouping

private struct GrammarSection: Identifiable {
    let id: Int
    let heading: String?
    let lines: [GrammarLine]

    static func group(_ lines: [GrammarLine]) -> [GrammarSection] {
        var sections: [GrammarSection] = []
        var currentHeading: String? = nil
        var currentLines: [GrammarLine] = []
        var sectionIndex = 0

        for line in lines {
            if case .heading(let text) = line.kind {
                // Flush the previous section
                if !currentLines.isEmpty || currentHeading != nil {
                    sections.append(GrammarSection(id: sectionIndex, heading: currentHeading, lines: currentLines))
                    sectionIndex += 1
                    currentLines = []
                }
                currentHeading = text
            } else {
                currentLines.append(line)
            }
        }

        // Flush final section
        if !currentLines.isEmpty || currentHeading != nil {
            sections.append(GrammarSection(id: sectionIndex, heading: currentHeading, lines: currentLines))
        }

        return sections
    }
}

// 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()
    }
}
