import SwiftUI

/// Lightweight block-aware Markdown renderer. SwiftUI's `AttributedString(markdown:)` is INLINE-only —
/// it silently drops block structure including GFM TABLES (the completed fixture's UCS-66 table would
/// vanish). This splits blocks by hand (headings, lists, pipe tables, fenced code, blockquote,
/// paragraphs) and renders each natively; inline emphasis/links use AttributedString per line.
/// Full CommonMark fidelity is a deferred dependency swap (swift-markdown / MarkdownUI).
public struct MarkdownView: View {
    let markdown: String
    public init(_ markdown: String) { self.markdown = markdown }

    public var body: some View {
        VStack(alignment: .leading, spacing: 10) {
            ForEach(Array(MarkdownBlock.parse(markdown).enumerated()), id: \.offset) { _, block in
                view(for: block)
            }
        }
    }

    @ViewBuilder
    private func view(for block: MarkdownBlock) -> some View {
        switch block {
        case .heading(let level, let text):
            inline(text)
                .font(headingFont(level))
                .foregroundStyle(Sage.ink)
                .padding(.top, level <= 2 ? 6 : 2)
        case .paragraph(let text):
            inline(text).font(.body).foregroundStyle(Sage.ink)
        case .listItem(let text):
            HStack(alignment: .top, spacing: 8) {
                Text("•").foregroundStyle(Sage.brand)
                inline(text).font(.body).foregroundStyle(Sage.ink)
            }
        case .quote(let text):
            inline(text)
                .font(.body).foregroundStyle(Sage.muted)
                .padding(.leading, 10)
                .overlay(Rectangle().fill(Sage.brand).frame(width: 3), alignment: .leading)
        case .code(let text):
            Text(text)
                .font(.system(.callout, design: .monospaced))
                .foregroundStyle(Sage.ink)
                .padding(10)
                .frame(maxWidth: .infinity, alignment: .leading)
                .background(Sage.surface, in: RoundedRectangle(cornerRadius: 8))
        case .table(let header, let rows):
            tableView(header: header, rows: rows)
        }
    }

    private func tableView(header: [String], rows: [[String]]) -> some View {
        let columns = max(header.count, rows.map(\.count).max() ?? 0)
        return VStack(spacing: 0) {
            tableRow(cells: header, columns: columns, isHeader: true)
            ForEach(Array(rows.enumerated()), id: \.offset) { _, row in
                tableRow(cells: row, columns: columns, isHeader: false)
            }
        }
        .background(Sage.card, in: RoundedRectangle(cornerRadius: 8))
        .overlay(RoundedRectangle(cornerRadius: 8).stroke(Sage.line))
    }

    private func tableRow(cells: [String], columns: Int, isHeader: Bool) -> some View {
        HStack(spacing: 0) {
            ForEach(0..<columns, id: \.self) { i in
                inline(i < cells.count ? cells[i] : "")
                    .font(isHeader ? .callout.weight(.semibold) : .callout)
                    .foregroundStyle(isHeader ? Sage.ink : Sage.muted)
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .padding(.horizontal, 8).padding(.vertical, 6)
            }
        }
        .background(isHeader ? Sage.surface : Sage.card)
        .overlay(Rectangle().fill(Sage.line).frame(height: 1), alignment: .bottom)
    }

    private func inline(_ s: String) -> Text {
        if let attr = try? AttributedString(markdown: s, options: .init(interpretedSyntax: .inlineOnlyPreservingWhitespace)) {
            return Text(attr)
        }
        return Text(s)
    }

    private func headingFont(_ level: Int) -> Font {
        switch level {
        case 1: return .title2.weight(.bold)
        case 2: return .title3.weight(.semibold)
        case 3: return .headline
        default: return .subheadline.weight(.semibold)
        }
    }
}

enum MarkdownBlock {
    case heading(Int, String)
    case paragraph(String)
    case listItem(String)
    case quote(String)
    case code(String)
    case table([String], [[String]])

    static func parse(_ md: String) -> [MarkdownBlock] {
        var blocks: [MarkdownBlock] = []
        let lines = md.components(separatedBy: "\n")
        var i = 0
        var para: [String] = []

        func flushPara() {
            if !para.isEmpty {
                blocks.append(.paragraph(para.joined(separator: " ")))
                para.removeAll()
            }
        }

        while i < lines.count {
            let raw = lines[i]
            let line = raw.trimmingCharacters(in: .whitespaces)

            // fenced code
            if line.hasPrefix("```") {
                flushPara()
                var code: [String] = []
                i += 1
                while i < lines.count, !lines[i].trimmingCharacters(in: .whitespaces).hasPrefix("```") {
                    code.append(lines[i]); i += 1
                }
                blocks.append(.code(code.joined(separator: "\n")))
                i += 1
                continue
            }

            // GFM pipe table: a header row followed by an alignment separator row (| --- | :--: |)
            if line.contains("|"), i + 1 < lines.count, isTableSeparator(lines[i + 1]) {
                flushPara()
                let header = splitRow(line)
                var rows: [[String]] = []
                i += 2 // skip header + separator (the separator is NOT data)
                while i < lines.count, lines[i].trimmingCharacters(in: .whitespaces).contains("|"),
                      !lines[i].trimmingCharacters(in: .whitespaces).isEmpty {
                    rows.append(splitRow(lines[i])); i += 1
                }
                blocks.append(.table(header, rows))
                continue
            }

            if line.isEmpty { flushPara(); i += 1; continue }

            if line.hasPrefix("#") {
                flushPara()
                let level = line.prefix(while: { $0 == "#" }).count
                let text = line.drop(while: { $0 == "#" }).trimmingCharacters(in: .whitespaces)
                blocks.append(.heading(min(level, 6), text))
                i += 1; continue
            }
            if line.hasPrefix("> ") {
                flushPara()
                blocks.append(.quote(String(line.dropFirst(2))))
                i += 1; continue
            }
            if line.hasPrefix("- ") || line.hasPrefix("* ") || line.hasPrefix("+ ") {
                flushPara()
                blocks.append(.listItem(String(line.dropFirst(2))))
                i += 1; continue
            }

            para.append(line)
            i += 1
        }
        flushPara()
        return blocks
    }

    /// A separator row is all dashes/colons/pipes/spaces, e.g. `| --- | :--: |`.
    private static func isTableSeparator(_ line: String) -> Bool {
        let t = line.trimmingCharacters(in: .whitespaces)
        guard t.contains("-"), t.contains("|") else { return false }
        return t.allSatisfy { $0 == "-" || $0 == ":" || $0 == "|" || $0 == " " }
    }

    private static func splitRow(_ line: String) -> [String] {
        var t = line.trimmingCharacters(in: .whitespaces)
        if t.hasPrefix("|") { t.removeFirst() }
        if t.hasSuffix("|") { t.removeLast() }
        return t.components(separatedBy: "|").map { $0.trimmingCharacters(in: .whitespaces) }
    }
}
