//
//  GameView.swift
//  MagicCounter
//
//  Created by Atridad Lahiji on 2025-12-06.
//

import SwiftUI
import UniformTypeIdentifiers
import Combine

/**
 * Main game screen displaying the grid of players.
 *
 * - match: The match record to initialize the game state from.
 */
struct GameView: View {
    @EnvironmentObject var gameManager: GameManager
    @State private var gameState: GameState
    @State private var showingStopConfirmation = false
    @State private var selectedPlayerForCommander: PlayerState?
    @State private var draggedPlayer: PlayerState?
    @State private var elapsedTime: TimeInterval = 0
    let match: MatchRecord

    init(match: MatchRecord) {
        self.match = match
        self._gameState = State(initialValue: match.state)
    }

    var body: some View {
        NavigationStack {
            GeometryReader { geometry in
                let columns = [GridItem(.adaptive(minimum: 320), spacing: 24)]
                ScrollView {
                    VStack(alignment: .leading, spacing: 24) {
                        // Custom Header
                        HStack(alignment: .center, spacing: 12) {
                            Text(match.name)
                                .font(.largeTitle.bold())
                                .lineLimit(1)
                                .truncationMode(.tail)
                                .foregroundStyle(.white)

                            if !gameState.stopped && gameState.winner == nil {
                                Text(timeString(from: elapsedTime))
                                    .font(.subheadline.bold())
                                    .monospacedDigit()
                                    .foregroundStyle(.green)
                                    .padding(.horizontal, 8)
                                    .padding(.vertical, 4)
                                    .background(.green.opacity(0.2))
                                    .clipShape(Capsule())
                            } else {
                                Text(timeString(from: match.lastUpdated.timeIntervalSince(match.startedAt)))
                                    .font(.subheadline.bold())
                                    .monospacedDigit()
                                    .foregroundStyle(.secondary)
                                    .padding(.horizontal, 8)
                                    .padding(.vertical, 4)
                                    .background(.secondary.opacity(0.2))
                                    .clipShape(Capsule())
                            }

                            Spacer()
                        }
                        .padding(.horizontal, 24)
                        .padding(.top, 16)

                        if gameState.stopped {
                            Text("Game Stopped")
                                .font(.headline)
                                .foregroundStyle(.red)
                                .padding(.horizontal, 24)
                        } else if let winner = gameState.winner {
                            Text("Winner: \(winner.name)")
                                .font(.headline)
                                .foregroundStyle(.green)
                                .padding(.horizontal, 24)
                        }

                        LazyVGrid(columns: columns, spacing: 24) {
                            ForEach(gameState.players) { player in
                                PlayerCell(
                                    player: player,
                                    gameState: gameState,
                                    isWinner: gameState.winner?.id == player.id,
                                    onUpdate: updatePlayer,
                                    onCommanderTap: { selectedPlayerForCommander = player },
                                    onScoop: { scoopPlayer(player) },
                                    onDragStart: {
                                        self.draggedPlayer = player
                                    }
                                )
                                .frame(height: 260)
                                .onDrop(of: [.text], delegate: PlayerDropDelegate(item: player, items: $gameState.players, draggedItem: $draggedPlayer))
                            }
                        }
                        .padding(.horizontal, 24)
                        .padding(.bottom, 24)
                    }
                }
            }
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .topBarLeading) {
                    Button(action: { gameManager.activeMatch = nil }) {
                        Image(systemName: "xmark")
                    }
                }

                ToolbarItem(placement: .topBarTrailing) {
                    if !gameState.stopped && gameState.winner == nil {
                        Button(action: { showingStopConfirmation = true }) {
                            Image(systemName: "stop.fill")
                        }
                    }
                }
            }
        }
        .onAppear {
            updateElapsedTime()
        }
        .onReceive(Timer.publish(every: 1, on: .main, in: .common).autoconnect()) { _ in
            updateElapsedTime()
        }
        .alert("Stop Game?", isPresented: $showingStopConfirmation) {
            Button("Cancel", role: .cancel) { }
            Button("Stop", role: .destructive) {
                gameManager.stopGame()
                gameState.stopped = true
            }
        } message: {
            Text("Are you sure you want to stop this game?")
        }
        .sheet(item: $selectedPlayerForCommander) { player in
            CommanderDamageView(
                targetPlayer: player,
                gameState: gameState,
                onUpdate: { updatedPlayer in
                    updatePlayer(player: updatedPlayer)
                }
            )
            .presentationDetents([.medium])
        }
        .onChange(of: gameState) { newState in
            if newState.winner != nil {
                Haptics.notification(.success)
            }
            gameManager.updateActiveGame(state: newState)
        }
    }

    private func updateElapsedTime() {
        if !gameState.stopped && gameState.winner == nil {
            elapsedTime = Date().timeIntervalSince(match.startedAt)
        } else {
            elapsedTime = match.lastUpdated.timeIntervalSince(match.startedAt)
        }
    }

    private func timeString(from timeInterval: TimeInterval) -> String {
        let hours = Int(timeInterval) / 3600
        let minutes = Int(timeInterval) / 60 % 60
        let seconds = Int(timeInterval) % 60
        if hours > 0 {
            return String(format: "%02i:%02i:%02i", hours, minutes, seconds)
        } else {
            return String(format: "%02i:%02i", minutes, seconds)
        }
    }

    private func updatePlayer(player: PlayerState) {
        if gameState.stopped || gameState.winner != nil { return }

        if let index = gameState.players.firstIndex(where: { $0.id == player.id }) {
            gameState.players[index] = player
        }

        // Update the sheet state if this is the player being edited to ensure the view refreshes
        if selectedPlayerForCommander?.id == player.id {
            selectedPlayerForCommander = player
        }
    }

    private func scoopPlayer(_ player: PlayerState) {
        if gameState.stopped || gameState.winner != nil { return }

        var newPlayer = player
        newPlayer.scooped = true
        updatePlayer(player: newPlayer)
    }
}

/**
 * Individual player cell component.
 *
 * - player: The player state to display.
 * - gameState: The global game state.
 * - isWinner: Whether this player is the winner.
 * - onUpdate: Callback to update the player state.
 * - onCommanderTap: Callback when commander damage button is tapped.
 * - onScoop: Callback when scoop action is triggered.
 */
struct PlayerCell: View {
    let player: PlayerState
    let gameState: GameState
    let isWinner: Bool
    let onUpdate: (PlayerState) -> Void
    let onCommanderTap: () -> Void
    let onScoop: () -> Void
    var onDragStart: (() -> Void)? = nil

    @State private var showScoopConfirmation = false

    var body: some View {
        ZStack {
            RoundedRectangle(cornerRadius: 24)
                .fill(.ultraThinMaterial)
                .shadow(color: isWinner ? .green.opacity(0.5) : .black.opacity(0.2), radius: isWinner ? 20 : 10, x: 0, y: 5)
                .overlay(
                    RoundedRectangle(cornerRadius: 24)
                        .stroke(isWinner ? Color.green : Color.clear, lineWidth: 3)
                )

            VStack(spacing: 12) {
                // Header - drag handle area
                HStack {
                    Text(player.name)
                        .font(.system(size: 22, weight: .semibold, design: .rounded))
                        .lineLimit(1)
                        .minimumScaleFactor(0.7)
                        .foregroundStyle(.primary)
                        .onDrag {
                            onDragStart?()
                            return NSItemProvider(object: String(player.id) as NSString)
                        }

                    Spacer()

                    Button(action: { showScoopConfirmation = true }) {
                        Image(systemName: "flag.fill")
                            .font(.system(size: 18))
                            .foregroundStyle(.secondary)
                            .padding(8)
                    }
                }
                .padding(.horizontal)
                .padding(.top, 12)

                // Life
                LifeCounterControl(
                    value: player.life,
                    onDecrease: { adjustLife(by: -1) },
                    onIncrease: { adjustLife(by: 1) },
                    onCustomDelta: { adjustLife(by: $0) }
                )

                Spacer()

                // Counters Row
                HStack(spacing: 12) {
                    if gameState.trackPoison {
                        SmallCounterControl(
                            value: player.poison,
                            icon: "drop.fill",
                            color: .purple,
                            onDecrease: { adjustPoison(by: -1) },
                            onIncrease: { adjustPoison(by: 1) },
                            onCustomDelta: { adjustPoison(by: $0) }
                        )
                    }

                    if gameState.trackCommanderDamage {
                        Button(action: onCommanderTap) {
                            VStack(spacing: 2) {
                                Image(systemName: "shield.fill")
                                    .font(.caption)
                                Text("CMD")
                                    .font(.caption2.bold())
                            }
                            .frame(width: 44, height: 44)
                            .background(Color.orange.opacity(0.1))
                            .foregroundStyle(.orange)
                            .clipShape(Circle())
                        }
                    }
                }
                .padding(.bottom, 16)
            }

            if player.isEliminated && !isWinner {
                ZStack {
                    Color.black.opacity(0.6)
                        .cornerRadius(24)
                    Image(systemName: "xmark")
                        .font(.system(size: 60, weight: .bold))
                        .foregroundStyle(.white.opacity(0.8))
                }
            }
        }
        .opacity(player.isEliminated && !isWinner ? 0.8 : 1)
        .scaleEffect(isWinner ? 1.05 : 1)
        .animation(.spring, value: isWinner)
        .alert("Scoop?", isPresented: $showScoopConfirmation) {
            Button("Cancel", role: .cancel) { }
            Button("Scoop", role: .destructive) {
                Haptics.notification(.warning)
                onScoop()
            }
        } message: {
            Text("Are you sure you want to scoop?")
        }
    }

    private func adjustLife(by amount: Int) {
        if player.isEliminated { return }
        Haptics.play(.light)
        var newPlayer = player
        newPlayer.life += amount
        onUpdate(newPlayer)

        if newPlayer.isEliminated && !player.isEliminated {
            Haptics.notification(.error)
        }
    }

    private func adjustPoison(by amount: Int) {
        if player.isEliminated { return }
        Haptics.play(.light)
        var newPlayer = player
        newPlayer.poison = max(0, newPlayer.poison + amount)
        onUpdate(newPlayer)

        if newPlayer.isEliminated && !player.isEliminated {
            Haptics.notification(.error)
        }
    }
}

/**
 * A row for managing commander damage from a single attacker.
 * Shows delta indicators and supports long-press for custom delta input.
 */
struct CommanderDamageRow: View {
    let attackerName: String
    let attackerId: Int
    let currentDamage: Int
    let onAdjust: (Int) -> Void

    @StateObject private var deltaTracker = DeltaTracker()
    @State private var showingCustomDeltaAlert = false
    @State private var customDeltaText = ""
    @State private var isIncreasing = true

    var body: some View {
        HStack {
            Text(attackerName)
                .font(.headline)
            Spacer()
            HStack(spacing: 16) {
                ZStack {
                    CircleButton(
                        icon: "minus",
                        color: .secondary,
                        action: {
                            onAdjust(-1)
                            withAnimation(.easeOut(duration: 0.15)) {
                                deltaTracker.addDelta(-1)
                            }
                        },
                        onLongPress: {
                            isIncreasing = false
                            customDeltaText = ""
                            showingCustomDeltaAlert = true
                            Haptics.play(.medium)
                        }
                    )
                    .buttonStyle(.borderless)

                    if deltaTracker.delta < 0 {
                        DeltaIndicator(delta: deltaTracker.delta, alignment: .trailing)
                            .offset(y: -30)
                    }
                }

                Text("\(currentDamage)")
                    .font(.title.bold())
                    .frame(minWidth: 40)
                    .multilineTextAlignment(.center)

                ZStack {
                    CircleButton(
                        icon: "plus",
                        color: .primary,
                        action: {
                            onAdjust(1)
                            withAnimation(.easeOut(duration: 0.15)) {
                                deltaTracker.addDelta(1)
                            }
                        },
                        onLongPress: {
                            isIncreasing = true
                            customDeltaText = ""
                            showingCustomDeltaAlert = true
                            Haptics.play(.medium)
                        }
                    )
                    .buttonStyle(.borderless)

                    if deltaTracker.delta > 0 {
                        DeltaIndicator(delta: deltaTracker.delta, alignment: .leading)
                            .offset(y: -30)
                    }
                }
            }
        }
        .padding(.vertical, 8)
        .alert("Enter Amount", isPresented: $showingCustomDeltaAlert) {
            TextField("Amount", text: $customDeltaText)
                .keyboardType(.numberPad)
            Button("Cancel", role: .cancel) { }
            Button(isIncreasing ? "Add" : "Subtract") {
                if let amount = Int(customDeltaText), amount > 0 {
                    let delta = isIncreasing ? amount : -amount
                    onAdjust(delta)
                    withAnimation(.easeOut(duration: 0.15)) {
                        deltaTracker.addDelta(delta)
                    }
                }
            }
        } message: {
            Text(isIncreasing ? "Enter the amount to add" : "Enter the amount to subtract")
        }
    }
}

/**
 * Sheet for managing commander damage received by a player.
 *
 * - targetPlayer: The player receiving damage.
 * - gameState: The global game state.
 * - onUpdate: Callback to update the player state.
 */
struct CommanderDamageView: View {
    @Environment(\.dismiss) var dismiss
    let targetPlayer: PlayerState
    let gameState: GameState
    let onUpdate: (PlayerState) -> Void

    init(targetPlayer: PlayerState, gameState: GameState, onUpdate: @escaping (PlayerState) -> Void) {
        self.targetPlayer = targetPlayer
        self.gameState = gameState
        self.onUpdate = onUpdate
    }

    var body: some View {
        NavigationStack {
            List {
                ForEach(gameState.players.filter { $0.id != targetPlayer.id }) { attacker in
                    CommanderDamageRow(
                        attackerName: attacker.name,
                        attackerId: attacker.id,
                        currentDamage: targetPlayer.commanderDamages[attacker.id] ?? 0,
                        onAdjust: { amount in
                            adjustCommanderDamage(attackerId: attacker.id, by: amount)
                        }
                    )
                }
            }
            .navigationTitle("Commander Damage to \(targetPlayer.name)")
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .confirmationAction) {
                    Button("Done") { dismiss() }
                }
            }
        }
    }

    private func adjustCommanderDamage(attackerId: Int, by amount: Int) {
        Haptics.play(.light)
        let wasEliminated = targetPlayer.isEliminated

        // Get current damage value
        let current = targetPlayer.commanderDamages[attackerId] ?? 0
        let newDamage = max(0, current + amount)

        // Only proceed if there's an actual change
        guard newDamage != current else { return }

        // Update commander damages
        var updatedPlayer = targetPlayer
        var damages = updatedPlayer.commanderDamages
        damages[attackerId] = newDamage
        updatedPlayer.commanderDamages = damages

        // Adjust life total by the damage change
        updatedPlayer.life -= amount

        // Notify parent to update
        onUpdate(updatedPlayer)

        if updatedPlayer.isEliminated && !wasEliminated {
            Haptics.notification(.error)
        }
    }
}

struct PlayerDropDelegate: DropDelegate {
    let item: PlayerState
    @Binding var items: [PlayerState]
    @Binding var draggedItem: PlayerState?

    func performDrop(info: DropInfo) -> Bool {
        return true
    }

    func dropEntered(info: DropInfo) {
        guard let draggedItem = draggedItem else { return }
        if draggedItem.id != item.id {
            if let from = items.firstIndex(where: { $0.id == draggedItem.id }),
               let to = items.firstIndex(where: { $0.id == item.id }) {
                withAnimation {
                    let movedItem = items.remove(at: from)
                    items.insert(movedItem, at: to)
                }
            }
        }
    }

    func dropUpdated(info: DropInfo) -> DropProposal? {
        return DropProposal(operation: .move)
    }
}
