import Foundation
import AppKit
import CoreImage
import Combine

@MainActor
class ScreenCaptureViewModel: ObservableObject {
    @Published var capturedImage: NSImage?
    @Published var croppedBoardImage: NSImage?
    @Published var captureError: CaptureError?
    @Published var isCapturing = false
    @Published var isBoardDetected = false
    @Published var isAutoCapturing = true  // Default to auto-capture mode
    
    private let screenCapture = ScreenCapture()
    private let boardDetector = BoardDetector()
    private var captureTask: Task<Void, Never>?
    private var monitorTask: Task<Void, Never>?
    // Share CIContext to avoid creating too many Metal command queues
    private static let shared = CIContext()
    private var context: CIContext { ScreenCaptureViewModel.shared }
    
    private var detectedBoardRect: CGRect?
    
    enum CaptureError: LocalizedError {
        case chessWindowNotFound
        case boardDetectionFailed
        case noBoardDetected
        
        var errorDescription: String? {
            switch self {
            case .chessWindowNotFound:
                return "Chess window not found"
            case .boardDetectionFailed:
                return "Failed to detect chess board"
            case .noBoardDetected:
                return "No chess board detected"
            }
        }
    }
    
    func startMonitoring() {
        guard monitorTask == nil else { return }
        
        monitorTask = Task {
            do {
                // Start continuous monitoring
                try await screenCapture.startCapture()
                
                monitorLoop: while !Task.isCancelled {
                    do {
                        if let image = screenCapture.getCurrentImage(),
                           let cgImage = image.cgImage(forProposedRect: nil, context: nil, hints: nil) {
                            // Check for board
                            let ciImage = CIImage(cgImage: cgImage)
                            if boardDetector.detectBoard(in: ciImage) != nil {
                                // Board detected, start capture if not already capturing
                                if !isCapturing {
                                    startCapture()
                                }
                            }
                        }
                        try await Task.sleep(nanoseconds: 500_000_000) // 0.5 seconds
                    } catch is CancellationError {
                        break monitorLoop
                    } catch {
                        // Just log the error and continue monitoring
                        print("Monitor error: \(error)")
                    }
                }
                
                try? await screenCapture.stopCapture()
            } catch {
                print("Monitor setup error: \(error)")
            }
        }
    }
    
    func stopMonitoring() {
        monitorTask?.cancel()
        monitorTask = nil
        stopCapture()
    }
    
    func startCapture() {
        guard !isCapturing else { return }
        
        isCapturing = true
        captureError = nil
        
        captureTask = Task {
            do {
                // Start continuous capture
                try await screenCapture.startCapture()
                
                captureLoop: while !Task.isCancelled {
                    do {
                        if let image = screenCapture.getCurrentImage() {
                            try await processImage(image)
                        }
                        try await Task.sleep(nanoseconds: 100_000_000) // 0.1 seconds
                    } catch is CancellationError {
                        break captureLoop
                    } catch {
                        // Just update the error state but continue capturing
                        if !Task.isCancelled {
                            handleCaptureError(error)
                            if isAutoCapturing && error as? CaptureError == .noBoardDetected {
                                // In auto-capture mode, stop capture but keep monitoring
                                stopCapture()
                                break captureLoop
                            }
                        }
                    }
                }
                
                // Clean up
                try? await screenCapture.stopCapture()
                if isCapturing {
                    isCapturing = false
                }
            } catch {
                handleCaptureError(error)
                isCapturing = false
            }
        }
    }
    
    func stopCapture() {
        captureTask?.cancel()
        captureTask = nil
        isCapturing = false
        captureError = nil
        isBoardDetected = false
    }
    
    private func processImage(_ image: NSImage) async throws {
        // Convert to CIImage for processing
        guard let cgImage = image.cgImage(forProposedRect: nil, context: nil, hints: nil) else {
            throw CaptureError.boardDetectionFailed
        }
        
        let ciImage = CIImage(cgImage: cgImage)
        
        // Detect board
        if let boardRect = boardDetector.detectBoard(in: ciImage) {
            // Board detected
            isBoardDetected = true
            self.detectedBoardRect = boardRect
            
            // Crop board image
            let croppedImage = ciImage.cropped(to: boardRect)
            updateImages(ciImage: ciImage, croppedImage: croppedImage)
        } else {
            // No board detected
            isBoardDetected = false
            self.detectedBoardRect = nil
            
            // Only update the full capture image
            if let cgImage = context.createCGImage(ciImage, from: ciImage.extent) {
                self.capturedImage = NSImage(cgImage: cgImage, size: .zero)
            }
            self.croppedBoardImage = nil
            
            if isAutoCapturing {
                throw CaptureError.noBoardDetected
            }
        }
    }
    
    private func updateImages(ciImage: CIImage, croppedImage: CIImage) {
        if let cgImage = context.createCGImage(ciImage, from: ciImage.extent) {
            let capturedNSImage = NSImage(cgImage: cgImage, size: .zero)
            self.capturedImage = capturedNSImage
        }
        
        if let cgCroppedImage = context.createCGImage(croppedImage, from: croppedImage.extent) {
            let croppedNSImage = NSImage(cgImage: cgCroppedImage, size: .zero)
            self.croppedBoardImage = croppedNSImage
        }
    }
    
    private func handleCaptureError(_ error: Error) {
        if let captureError = error as? CaptureError {
            self.captureError = captureError
        } else {
            self.captureError = .boardDetectionFailed
        }
    }
}
