import SwiftUI
import SharedModels
import SwiftData

struct SentenceBuilderView: View {
    @Environment(\.modelContext) private var modelContext
    @Environment(\.cloudModelContextProvider) private var cloudModelContextProvider
    private var cloudModelContext: ModelContext { cloudModelContextProvider() }

    @State private var currentCard: VocabCard?
    @State private var exampleIndex: Int = 0
    @State private var shuffledWords: [IndexedWord] = []
    @State private var selectedWords: [IndexedWord] = []
    @State private var hasChecked: Bool = false
    @State private var isCorrect: Bool = false
    @State private var sessionCorrect: Int = 0
    @State private var sessionTotal: Int = 0
    @State private var speechService = SpeechService()
    @State private var isLoading = false
    @State private var hasAvailableSentences = true

    private var currentSpanish: String {
        guard let card = currentCard,
              exampleIndex < card.examplesES.count else { return "" }
        return card.examplesES[exampleIndex]
    }

    private var currentEnglish: String {
        guard let card = currentCard,
              exampleIndex < card.examplesEN.count else { return "" }
        return card.examplesEN[exampleIndex]
    }

    private var correctWords: [String] {
        currentSpanish
            .components(separatedBy: " ")
            .filter { !$0.isEmpty }
    }

    var body: some View {
        ScrollView {
            VStack(spacing: 24) {
                if isLoading && currentCard == nil {
                    ProgressView("Loading sentence...")
                        .frame(maxWidth: .infinity, minHeight: 220)
                } else if !hasAvailableSentences {
                    ContentUnavailableView(
                        "No Sentences Available",
                        systemImage: "text.word.spacing",
                        description: Text("Complete some course lessons to unlock sentence practice.")
                    )
                } else if currentCard != nil {
                    // Stats
                    if sessionTotal > 0 {
                        HStack(spacing: 16) {
                            Label("\(sessionCorrect)/\(sessionTotal)", systemImage: "checkmark.circle.fill")
                                .foregroundStyle(.green)
                            if sessionTotal > 0 {
                                Text("\(Int(Double(sessionCorrect) / Double(sessionTotal) * 100))%")
                                    .foregroundStyle(.secondary)
                            }
                        }
                        .font(.subheadline.weight(.medium))
                    }

                    // English prompt
                    VStack(spacing: 8) {
                        Label("Translate this sentence", systemImage: "globe")
                            .font(.subheadline.weight(.medium))
                            .foregroundStyle(.secondary)

                        Text(currentEnglish)
                            .font(.title3.weight(.semibold))
                            .multilineTextAlignment(.center)
                            .frame(maxWidth: .infinity)
                            .padding()
                            .glassEffect(in: RoundedRectangle(cornerRadius: 16))

                        Button {
                            speechService.speak(currentSpanish)
                        } label: {
                            Label("Listen", systemImage: "speaker.wave.2.fill")
                                .font(.subheadline)
                        }
                        .buttonStyle(.borderless)
                        .tint(.blue)
                    }

                    // Selected words area (answer)
                    VStack(spacing: 8) {
                        Text("Your answer:")
                            .font(.caption.weight(.medium))
                            .foregroundStyle(.secondary)
                            .frame(maxWidth: .infinity, alignment: .leading)

                        answerArea
                    }

                    // Available word tiles
                    VStack(spacing: 8) {
                        Text("Available words:")
                            .font(.caption.weight(.medium))
                            .foregroundStyle(.secondary)
                            .frame(maxWidth: .infinity, alignment: .leading)

                        wordBank
                    }

                    // Action buttons
                    actionButtons
                }
            }
            .padding()
            .adaptiveContainer()
        }
        .onAppear {
            if currentCard == nil {
                loadRandomSentence()
            }
        }
    }

    // MARK: - Answer Area

    private var answerArea: some View {
        let borderColor: Color = hasChecked ? (isCorrect ? .green : .red) : .secondary.opacity(0.3)

        return FlowLayout(spacing: 8) {
            ForEach(selectedWords) { word in
                Button {
                    if !hasChecked {
                        withAnimation(.smooth(duration: 0.25)) {
                            removeWord(word)
                        }
                    }
                } label: {
                    Text(word.text)
                        .font(.body.weight(.medium))
                        .padding(.horizontal, 14)
                        .padding(.vertical, 8)
                        .background(.fill.tertiary, in: RoundedRectangle(cornerRadius: 10))
                }
                .tint(.primary)
                .transition(.scale.combined(with: .opacity))
            }
        }
        .frame(maxWidth: .infinity, minHeight: 60, alignment: .topLeading)
        .padding(12)
        .overlay(
            RoundedRectangle(cornerRadius: 14)
                .strokeBorder(borderColor, lineWidth: 2)
        )
        .animation(.smooth(duration: 0.3), value: hasChecked)
    }

    // MARK: - Word Bank

    private var wordBank: some View {
        FlowLayout(spacing: 8) {
            ForEach(shuffledWords) { word in
                let isSelected = selectedWords.contains(where: { $0.id == word.id })

                Button {
                    if !hasChecked && !isSelected {
                        withAnimation(.smooth(duration: 0.25)) {
                            selectWord(word)
                        }
                    }
                } label: {
                    Text(word.text)
                        .font(.body.weight(.medium))
                        .padding(.horizontal, 14)
                        .padding(.vertical, 8)
                        .background(
                            isSelected ? Color.clear : Color.accentColor.opacity(0.12),
                            in: RoundedRectangle(cornerRadius: 10)
                        )
                        .overlay(
                            RoundedRectangle(cornerRadius: 10)
                                .strokeBorder(isSelected ? Color.secondary.opacity(0.2) : Color.accentColor.opacity(0.3), lineWidth: 1)
                        )
                }
                .tint(isSelected ? .secondary.opacity(0.4) : .primary)
                .disabled(isSelected)
                .transition(.scale.combined(with: .opacity))
            }
        }
        .frame(maxWidth: .infinity, alignment: .topLeading)
        .padding(12)
    }

    // MARK: - Action Buttons

    private var actionButtons: some View {
        VStack(spacing: 12) {
            if hasChecked {
                // Show result
                VStack(spacing: 8) {
                    if isCorrect {
                        Label("Correct!", systemImage: "checkmark.circle.fill")
                            .font(.headline)
                            .foregroundStyle(.green)
                    } else {
                        Label("Not quite", systemImage: "xmark.circle.fill")
                            .font(.headline)
                            .foregroundStyle(.red)

                        VStack(spacing: 4) {
                            Text("Correct answer:")
                                .font(.caption)
                                .foregroundStyle(.secondary)
                            Text(currentSpanish)
                                .font(.body.weight(.semibold))
                                .italic()
                        }
                        .padding()
                        .frame(maxWidth: .infinity)
                        .glassEffect(in: RoundedRectangle(cornerRadius: 12))
                    }
                }
                .transition(.move(edge: .bottom).combined(with: .opacity))

                // Navigation
                HStack(spacing: 16) {
                    Button {
                        withAnimation { retrySentence() }
                    } label: {
                        Label("Retry", systemImage: "arrow.counterclockwise")
                            .frame(maxWidth: .infinity)
                    }
                    .buttonStyle(.bordered)

                    Button {
                        withAnimation { loadRandomSentence() }
                    } label: {
                        Label("Next", systemImage: "arrow.right")
                            .frame(maxWidth: .infinity)
                    }
                    .buttonStyle(.borderedProminent)
                }
            } else {
                HStack(spacing: 16) {
                    Button {
                        withAnimation { clearAnswer() }
                    } label: {
                        Label("Clear", systemImage: "trash")
                            .frame(maxWidth: .infinity)
                    }
                    .buttonStyle(.bordered)
                    .disabled(selectedWords.isEmpty)

                    Button {
                        withAnimation { checkAnswer() }
                    } label: {
                        Label("Check", systemImage: "checkmark")
                            .frame(maxWidth: .infinity)
                    }
                    .buttonStyle(.borderedProminent)
                    .disabled(selectedWords.count != correctWords.count)
                }
            }
        }
        .animation(.smooth, value: hasChecked)
    }

    // MARK: - Logic

    private func loadRandomSentence() {
        isLoading = true
        hasChecked = false
        isCorrect = false
        selectedWords = []
        defer { isLoading = false }

        guard let selection = fetchRandomSentenceSelection() else {
            currentCard = nil
            shuffledWords = []
            hasAvailableSentences = false
            return
        }

        hasAvailableSentences = true
        currentCard = selection.card
        exampleIndex = selection.exampleIndex

        let words = selection.spanish
            .components(separatedBy: " ")
            .filter { !$0.isEmpty }

        shuffledWords = words.enumerated().map { IndexedWord(index: $0.offset, text: $0.element) }
        shuffledWords.shuffle()
    }

    private func retrySentence() {
        hasChecked = false
        isCorrect = false
        selectedWords = []
        shuffledWords.shuffle()
    }

    private func clearAnswer() {
        selectedWords = []
    }

    private func selectWord(_ word: IndexedWord) {
        selectedWords.append(word)
    }

    private func removeWord(_ word: IndexedWord) {
        selectedWords.removeAll { $0.id == word.id }
    }

    private func checkAnswer() {
        let userSentence = selectedWords.map(\.text).joined(separator: " ")
        isCorrect = userSentence == currentSpanish
        hasChecked = true
        sessionTotal += 1
        if isCorrect {
            sessionCorrect += 1
        }
        ReviewStore.recordActivity(context: cloudModelContext)
    }

    private func fetchRandomSentenceSelection() -> (card: VocabCard, exampleIndex: Int, spanish: String)? {
        let totalCards = CourseCardStore.fetchCardCount(context: modelContext)
        guard totalCards > 0 else { return nil }

        let batchSize = 32
        let maxOffset = max(totalCards - batchSize, 0)
        var attemptedOffsets: Set<Int> = []

        for _ in 0..<8 {
            let offset = maxOffset > 0 ? Int.random(in: 0...maxOffset) : 0
            guard attemptedOffsets.insert(offset).inserted else { continue }

            let batch = CourseCardStore.fetchCards(offset: offset, limit: batchSize, context: modelContext)
            if let selection = sentenceSelection(in: batch) {
                return selection
            }
        }

        for offset in stride(from: 0, to: totalCards, by: batchSize) {
            let batch = CourseCardStore.fetchCards(offset: offset, limit: batchSize, context: modelContext)
            if let selection = sentenceSelection(in: batch) {
                return selection
            }
        }

        return nil
    }

    private func sentenceSelection(in cards: [VocabCard]) -> (card: VocabCard, exampleIndex: Int, spanish: String)? {
        for card in cards.shuffled() {
            let indices = validExampleIndices(in: card)
            guard let index = indices.randomElement() else { continue }
            return (card, index, card.examplesES[index])
        }

        return nil
    }

    private func validExampleIndices(in card: VocabCard) -> [Int] {
        guard !card.examplesES.isEmpty,
              card.examplesES.count == card.examplesEN.count else { return [] }

        return Array(zip(card.examplesES.indices, zip(card.examplesES, card.examplesEN)))
            .compactMap { index, pair in
                isValidSentencePair(spanish: pair.0, english: pair.1) ? index : nil
            }
    }

    private func isValidSentencePair(spanish: String, english: String) -> Bool {
        let junkPatterns = [
            "ADVERB", "ADJECTIVE", "NOUN", "VERB", "PHRASE", "FEMININE", "MASCULINE",
            "PRONOUN", "PREPOSITION", "CONJUNCTION", "INTERJECTION", "TRANSITIVE",
            "INTRANSITIVE", "REFLEXIVE", "IMPERSONAL",
        ]

        let words = spanish.components(separatedBy: " ")
        let isLongEnough = words.count >= 4
        let isNotJunk = !junkPatterns.contains {
            english.uppercased().hasPrefix($0) || spanish.uppercased().hasPrefix($0)
        }
        let hasNoParens = !spanish.contains("(")
        return isLongEnough && isNotJunk && hasNoParens
    }
}

// MARK: - Indexed Word

private struct IndexedWord: Identifiable, Equatable {
    let index: Int
    let text: String
    var id: Int { index }
}

// MARK: - Flow Layout

private struct FlowLayout: Layout {
    var spacing: CGFloat = 8

    func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
        let rows = computeRows(proposal: proposal, subviews: subviews)
        var height: CGFloat = 0
        for (i, row) in rows.enumerated() {
            let rowHeight = row.map { $0.sizeThatFits(.unspecified).height }.max() ?? 0
            height += rowHeight
            if i < rows.count - 1 { height += 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

        for row in rows {
            let rowHeight = row.map { $0.sizeThatFits(.unspecified).height }.max() ?? 0
            var x = bounds.minX

            for subview in row {
                let size = subview.sizeThatFits(.unspecified)
                subview.place(at: CGPoint(x: x, y: y), proposal: ProposedViewSize(size))
                x += size.width + spacing
            }

            y += rowHeight + spacing
        }
    }

    private func computeRows(proposal: ProposedViewSize, subviews: Subviews) -> [[LayoutSubviews.Element]] {
        let maxWidth = proposal.width ?? .infinity
        var rows: [[LayoutSubviews.Element]] = [[]]
        var currentRowWidth: CGFloat = 0

        for subview in subviews {
            let size = subview.sizeThatFits(.unspecified)
            if currentRowWidth + size.width + spacing > maxWidth && !rows[rows.count - 1].isEmpty {
                rows.append([])
                currentRowWidth = 0
            }
            rows[rows.count - 1].append(subview)
            currentRowWidth += size.width + spacing
        }

        return rows
    }
}

#Preview {
    NavigationStack {
        SentenceBuilderView()
    }
    .modelContainer(for: VocabCard.self, inMemory: true)
}
