import SwiftUI
import AVFoundation
import VisionKit

struct BarcodeScannerView: View {
    let onScanned: (String) -> Void
    @State private var cameraAuthorized = AVCaptureDevice.authorizationStatus(for: .video) == .authorized

    var body: some View {
        Group {
            if cameraAuthorized {
                if DataScannerViewController.isSupported, DataScannerViewController.isAvailable {
                    DataScannerRepresentable(onScanned: onScanned)
                } else {
                    AVScannerRepresentable(onScanned: onScanned)
                }
            } else {
                scannerUnavailableView
            }
        }
        .background(Color.black.opacity(0.85))
        .overlay {
            RoundedRectangle(cornerRadius: 16)
                .stroke(Color.white.opacity(0.75), lineWidth: 2)
        }
        .accessibilityLabel("ISBN barkod tarayıcı")
        .onAppear {
            requestCameraPermissionIfNeeded()
        }
    }

    private var scannerUnavailableView: some View {
        VStack(spacing: 10) {
            Image(systemName: "camera.viewfinder")
                .font(.system(size: 30))
                .foregroundStyle(.white.opacity(0.9))
            Text("Kamera erişimi gerekli")
                .font(.headline)
                .foregroundStyle(.white)
            Text("Barkod taramak için Ayarlar > Gizlilik > Kamera üzerinden izin verin.")
                .font(.footnote)
                .foregroundStyle(.white.opacity(0.85))
                .multilineTextAlignment(.center)
                .padding(.horizontal, 12)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    }

    private func requestCameraPermissionIfNeeded() {
        let status = AVCaptureDevice.authorizationStatus(for: .video)
        switch status {
        case .authorized:
            cameraAuthorized = true
        case .notDetermined:
            AVCaptureDevice.requestAccess(for: .video) { granted in
                DispatchQueue.main.async {
                    cameraAuthorized = granted
                }
            }
        default:
            cameraAuthorized = false
        }
    }
}

@available(iOS 16.0, *)
private struct DataScannerRepresentable: UIViewControllerRepresentable {
    let onScanned: (String) -> Void

    func makeCoordinator() -> Coordinator { Coordinator(onScanned: onScanned) }

    func makeUIViewController(context: Context) -> DataScannerViewController {
        let vc = DataScannerViewController(
            recognizedDataTypes: [.barcode(symbologies: [.ean8, .ean13, .upce, .code128])],
            qualityLevel: .balanced,
            recognizesMultipleItems: false,
            isHighFrameRateTrackingEnabled: true,
            isPinchToZoomEnabled: true,
            isGuidanceEnabled: true,
            isHighlightingEnabled: true
        )
        vc.delegate = context.coordinator
        try? vc.startScanning()
        return vc
    }

    func updateUIViewController(_ uiViewController: DataScannerViewController, context: Context) {}

    final class Coordinator: NSObject, DataScannerViewControllerDelegate {
        let onScanned: (String) -> Void
        private var lastISBN: String?
        private var lastEmitAt: Date = .distantPast

        init(onScanned: @escaping (String) -> Void) {
            self.onScanned = onScanned
        }

        func dataScanner(_ dataScanner: DataScannerViewController, didTapOn item: RecognizedItem) {
            guard case .barcode(let code) = item,
                  let payload = code.payloadStringValue,
                  let normalized = ISBNNormalizer.normalize(payload) else { return }
            emitIfNeeded(normalized)
        }

        func dataScanner(_ dataScanner: DataScannerViewController, didAdd addedItems: [RecognizedItem], allItems: [RecognizedItem]) {
            guard let first = addedItems.first,
                  case .barcode(let code) = first,
                  let payload = code.payloadStringValue,
                  let normalized = ISBNNormalizer.normalize(payload) else { return }
            emitIfNeeded(normalized)
        }

        private func emitIfNeeded(_ isbn: String) {
            let now = Date()
            if lastISBN == isbn, now.timeIntervalSince(lastEmitAt) < 1.5 {
                return
            }
            lastISBN = isbn
            lastEmitAt = now
            onScanned(isbn)
        }
    }
}

private struct AVScannerRepresentable: UIViewRepresentable {
    let onScanned: (String) -> Void

    func makeUIView(context: Context) -> ScannerPreviewView {
        let view = ScannerPreviewView()
        context.coordinator.configure(preview: view)
        return view
    }

    func updateUIView(_ uiView: ScannerPreviewView, context: Context) {}

    func makeCoordinator() -> Coordinator { Coordinator(onScanned: onScanned) }

    final class Coordinator: NSObject, AVCaptureMetadataOutputObjectsDelegate {
        private let session = AVCaptureSession()
        private let sessionQueue = DispatchQueue(label: "bookibra.av.capture.session")
        private let onScanned: (String) -> Void
        private var lastISBN: String?
        private var lastEmitAt: Date = .distantPast

        init(onScanned: @escaping (String) -> Void) {
            self.onScanned = onScanned
        }

        func configure(preview: ScannerPreviewView) {
            guard let device = AVCaptureDevice.default(for: .video),
                  let input = try? AVCaptureDeviceInput(device: device) else { return }

            if session.canAddInput(input) { session.addInput(input) }

            let output = AVCaptureMetadataOutput()
            if session.canAddOutput(output) {
                session.addOutput(output)
                output.setMetadataObjectsDelegate(self, queue: .main)
                output.metadataObjectTypes = [.ean8, .ean13, .upce, .code128]
            }

            preview.previewLayer.session = session
            preview.previewLayer.videoGravity = .resizeAspectFill
            sessionQueue.async { [session] in
                session.startRunning()
            }
        }

        func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
            guard let code = metadataObjects.compactMap({ $0 as? AVMetadataMachineReadableCodeObject }).first,
                  let value = code.stringValue,
                  let normalized = ISBNNormalizer.normalize(value) else { return }
            let now = Date()
            if lastISBN == normalized, now.timeIntervalSince(lastEmitAt) < 1.5 {
                return
            }
            lastISBN = normalized
            lastEmitAt = now
            onScanned(normalized)
        }
    }
}

private final class ScannerPreviewView: UIView {
    override class var layerClass: AnyClass { AVCaptureVideoPreviewLayer.self }
    var previewLayer: AVCaptureVideoPreviewLayer { layer as! AVCaptureVideoPreviewLayer }
}

enum ISBNNormalizer {
    static func normalize(_ value: String) -> String? {
        let cleaned = value.uppercased().filter { $0.isNumber || $0 == "X" }
        if cleaned.count == 13 { return cleaned }
        if cleaned.count == 10 { return cleaned }
        return nil
    }
}
