import SwiftUI
import SwiftData
import SharedModels

struct LyricsReaderView: View {
    let song: SavedSong

    @Environment(DictionaryService.self) private var dictionary
    @Environment(\.cloudModelContextProvider) private var cloudModelContextProvider
    private var cloudModelContext: ModelContext { cloudModelContextProvider() }
    @State private var selectedWord: LyricsWordLookup?
    @State private var lookupCache: [String: LyricsWordLookup] = [:]

    var body: some View {
        ScrollView {
            VStack(spacing: 20) {
                headerSection
                lyricsBody
            }
            .padding()
            .adaptiveContainer()
        }
        .navigationTitle(song.title)
        .navigationBarTitleDisplayMode(.inline)
        .sheet(item: $selectedWord) { word in
            LyricsWordDetailSheet(word: word)
                .presentationDetents([.height(260)])
        }
    }

    // MARK: - Header

    private var headerSection: some View {
        VStack(spacing: 10) {
            if !song.albumArtURL.isEmpty, let url = URL(string: song.albumArtURL) {
                AsyncImage(url: url) { image in
                    image.resizable().scaledToFill()
                } placeholder: {
                    RoundedRectangle(cornerRadius: 12)
                        .fill(.fill.quaternary)
                }
                .frame(width: 160, height: 160)
                .clipShape(RoundedRectangle(cornerRadius: 12))
            }

            Text(song.title)
                .font(.title2.weight(.bold))
                .multilineTextAlignment(.center)

            Text(song.artist)
                .font(.subheadline)
                .foregroundStyle(.secondary)

            if !song.appleMusicURL.isEmpty, let url = URL(string: song.appleMusicURL) {
                Link(destination: url) {
                    Label("Open in Apple Music", systemImage: "apple.logo")
                        .font(.caption.weight(.medium))
                }
                .tint(.pink)
            }
        }
    }

    // MARK: - Lyrics Body

    private var lyricsBody: some View {
        let spanishLines = song.lyricsES.components(separatedBy: "\n")
        let englishLines = song.lyricsEN.components(separatedBy: "\n")
        let lineCount = max(spanishLines.count, englishLines.count)

        return VStack(alignment: .leading, spacing: 0) {
            ForEach(0..<lineCount, id: \.self) { index in
                let es = index < spanishLines.count ? spanishLines[index] : ""
                let en = index < englishLines.count ? englishLines[index] : ""

                if es.trimmingCharacters(in: .whitespaces).isEmpty &&
                   en.trimmingCharacters(in: .whitespaces).isEmpty {
                    // Blank line = section divider
                    Spacer().frame(height: 20)
                } else {
                    VStack(alignment: .leading, spacing: 2) {
                        if !es.isEmpty {
                            spanishLine(es)
                        }
                        if !en.isEmpty {
                            Text(en)
                                .font(.caption)
                                .foregroundStyle(.secondary)
                        }
                    }
                    .padding(.vertical, 4)
                }
            }
        }
        .padding()
        .background(.fill.quinary, in: RoundedRectangle(cornerRadius: 12))
    }

    private func spanishLine(_ line: String) -> some View {
        let tokens = line.components(separatedBy: " ")
        return LyricsFlowLayout(spacing: 0) {
            ForEach(Array(tokens.enumerated()), id: \.offset) { _, token in
                LyricsWordView(token: token, lookup: makeLookup(for: token)) { word in
                    ReviewStore.recordActivity(context: cloudModelContext)
                    selectedWord = word
                }
            }
        }
    }

    // MARK: - Lookup

    private func makeLookup(for rawToken: String) -> LyricsWordLookup? {
        let cleaned = rawToken.lowercased()
            .trimmingCharacters(in: .punctuationCharacters)
            .trimmingCharacters(in: .whitespaces)
        guard !cleaned.isEmpty else { return nil }

        if let cached = lookupCache[cleaned] { return cached }
        guard let entry = dictionary.lookup(cleaned) else { return nil }

        let displayWord = rawToken
            .trimmingCharacters(in: .punctuationCharacters)
            .trimmingCharacters(in: .whitespaces)

        let tenseDisplay = entry.tenseId.flatMap { TenseInfo.find($0)?.english }

        let lookup = LyricsWordLookup(
            word: displayWord.isEmpty ? entry.word : displayWord,
            baseForm: entry.baseForm,
            english: entry.english,
            partOfSpeech: entry.partOfSpeech,
            tenseDisplay: tenseDisplay,
            person: entry.person
        )
        lookupCache[cleaned] = lookup
        return lookup
    }
}

// MARK: - Word Lookup Model

private struct LyricsWordLookup: Identifiable, Hashable {
    let word: String
    let baseForm: String
    let english: String
    let partOfSpeech: String
    let tenseDisplay: String?
    let person: String?

    var id: String { word }
}

// MARK: - Word View

private struct LyricsWordView: View {
    let token: String
    let lookup: LyricsWordLookup?
    let onLookup: (LyricsWordLookup) -> Void

    var body: some View {
        Text(token + " ")
            .font(.body.weight(.medium))
            .foregroundStyle(.primary)
            .underline(lookup != nil, color: .teal.opacity(0.35))
            .contentShape(Rectangle())
            .onLongPressGesture(minimumDuration: 0.35) {
                if let lookup {
                    onLookup(lookup)
                }
            }
    }
}

// MARK: - Detail Sheet

private struct LyricsWordDetailSheet: View {
    let word: LyricsWordLookup

    var body: some View {
        VStack(alignment: .leading, spacing: 14) {
            HStack {
                Text(word.word)
                    .font(.title2.bold())
                Spacer()
                if !word.partOfSpeech.isEmpty {
                    Text(word.partOfSpeech)
                        .font(.caption.weight(.medium))
                        .foregroundStyle(.secondary)
                        .padding(.horizontal, 8)
                        .padding(.vertical, 4)
                        .background(.fill.tertiary, in: Capsule())
                }
            }

            Divider()

            VStack(alignment: .leading, spacing: 10) {
                if !word.baseForm.isEmpty && word.baseForm.lowercased() != word.word.lowercased() {
                    detailRow(label: "Base form", value: word.baseForm, italic: true)
                }

                if !word.english.isEmpty {
                    detailRow(label: "English", value: word.english)
                }

                if let tenseDisplay = word.tenseDisplay {
                    let personSuffix = (word.person?.isEmpty == false) ? " · \(word.person!)" : ""
                    detailRow(label: "Tense", value: tenseDisplay + personSuffix)
                }
            }

            Spacer(minLength: 0)
        }
        .padding()
        .frame(maxWidth: .infinity, alignment: .leading)
    }

    @ViewBuilder
    private func detailRow(label: String, value: String, italic: Bool = false) -> some View {
        HStack(alignment: .firstTextBaseline, spacing: 8) {
            Text("\(label):")
                .font(.subheadline)
                .foregroundStyle(.secondary)
                .frame(width: 86, alignment: .leading)
            Text(value)
                .font(.subheadline.weight(.semibold))
                .italic(italic)
            Spacer(minLength: 0)
        }
    }
}

// MARK: - Flow Layout

private struct LyricsFlowLayout: Layout {
    var spacing: CGFloat = 0

    func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
        let rows = computeRows(proposal: proposal, subviews: subviews)
        var height: CGFloat = 0
        for row in rows { height += row.map { $0.height }.max() ?? 0 }
        height += CGFloat(max(0, rows.count - 1)) * spacing
        return CGSize(width: proposal.width ?? 0, height: height)
    }

    func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
        let rows = computeRows(proposal: proposal, subviews: subviews)
        var y = bounds.minY
        var idx = 0
        for row in rows {
            var x = bounds.minX
            let rh = row.map { $0.height }.max() ?? 0
            for size in row {
                subviews[idx].place(at: CGPoint(x: x, y: y), proposal: ProposedViewSize(size))
                x += size.width
                idx += 1
            }
            y += rh + spacing
        }
    }

    private func computeRows(proposal: ProposedViewSize, subviews: Subviews) -> [[CGSize]] {
        let mw = proposal.width ?? .infinity
        var rows: [[CGSize]] = [[]]
        var cw: CGFloat = 0
        for sv in subviews {
            let s = sv.sizeThatFits(.unspecified)
            if cw + s.width > mw && !rows[rows.count - 1].isEmpty {
                rows.append([])
                cw = 0
            }
            rows[rows.count - 1].append(s)
            cw += s.width
        }
        return rows
    }
}
