import QRCode from "qrcode";

// Web USB type definitions
interface USBDevice {
  vendorId: number;
  productId: number;
  manufacturerName?: string;
  productName?: string;
  serialNumber?: string;
  open(): Promise<void>;
  close(): Promise<void>;
  selectConfiguration(configurationValue: number): Promise<void>;
  claimInterface(interfaceNumber: number): Promise<void>;
  releaseInterface(interfaceNumber: number): Promise<void>;
  transferOut(
    endpointNumber: number,
    data: BufferSource,
  ): Promise<USBOutTransferResult>;
  configuration: {
    interfaces: Array<{
      interfaceNumber: number;
      alternates: Array<{
        endpoints: Array<{
          endpointNumber: number;
          direction: "in" | "out";
        }>;
      }>;
    }>;
  };
}

interface USBOutTransferResult {
  status: "ok" | "stall" | "babble";
  bytesWritten: number;
}

declare global {
  interface Navigator {
    usb: {
      getDevices(): Promise<USBDevice[]>;
      requestDevice(options: {
        filters: Array<{ vendorId?: number; productId?: number }>;
      }): Promise<USBDevice>;
    };
  }
}

export default function useWebUsb() {
  const isWebUsbSupported = computed(() => {
    return typeof navigator !== "undefined" && "usb" in navigator;
  });

  const getAvailableDevices = async () => {
    if (!isWebUsbSupported.value) {
      throw new Error("Web USB is not supported in this browser");
    }
    try {
      const devices = await navigator.usb.getDevices();
      return devices.map((device: USBDevice) => ({
        vendorId: device.vendorId,
        productId: device.productId,
        manufacturerName: device.manufacturerName || "Unknown",
        productName: device.productName || "Unknown",
        serialNumber: device.serialNumber || "Unknown",
        deviceId: `${device.vendorId}:${device.productId}`,
      }));
    } catch (error) {
      console.error("Error getting USB devices:", error);
      throw new Error("Failed to get USB devices");
    }
  };

  const requestDevice = async () => {
    if (!isWebUsbSupported.value) {
      throw new Error("Web USB is not supported in this browser");
    }
    try {
      const device = await navigator.usb.requestDevice({ filters: [] });
      return {
        vendorId: device.vendorId,
        productId: device.productId,
        manufacturerName: device.manufacturerName || "Unknown",
        productName: device.productName || "Unknown",
        serialNumber: device.serialNumber || "Unknown",
        deviceId: `${device.vendorId}:${device.productId}`,
      };
    } catch (error: any) {
      if (error.name === "NotFoundError") {
        throw new Error("No device selected");
      }
      console.error("Error requesting USB device:", error);
      throw new Error("Failed to access USB device");
    }
  };

  const connectToDevice = async (vendorId: number, productId: number) => {
    if (!isWebUsbSupported.value) {
      throw new Error("Web USB is not supported in this browser");
    }
    try {
      let devices = await navigator.usb.getDevices();
      console.log("[WebUSB] Available devices:", devices.map((d: USBDevice) => `${d.vendorId}:${d.productId}`));
      let device = devices.find(
        (d: USBDevice) => d.vendorId === vendorId && d.productId === productId,
      );
      if (!device) {
        console.log("[WebUSB] Device not in granted list, requesting permission for", vendorId, productId);
        device = await navigator.usb.requestDevice({
          filters: [{ vendorId, productId }],
        });
      }
      console.log("[WebUSB] Opening device:", device.vendorId, device.productId);
      await device.open();
      await device.selectConfiguration(1);
      await device.claimInterface(0);
      console.log("[WebUSB] Device opened and interface claimed.");
      return device;
    } catch (error) {
      console.error("Error connecting to USB device:", error);
      throw new Error("Failed to connect to USB device");
    }
  };

  const testConnection = async (
    vendorId: number,
    productId: number,
    forQr?: boolean,
    urlImage?: string,
    doorLevel?: number | string | null,
    liftLevel?: number | string | null,
    companyName?: string,
    address?: string,
    qrHeader?: string,
    qrSubText?: string,
  ) => {
    if (!isWebUsbSupported.value) {
      throw new Error("Web USB is not supported in this browser");
    }

    let device: USBDevice | null = null;
    try {
      device = await connectToDevice(vendorId, productId);

      const deviceInfo = {
        vendorId: device.vendorId,
        productId: device.productId,
        manufacturerName: device.manufacturerName || "Unknown",
        productName: device.productName || "Unknown",
        serialNumber: device.serialNumber || "Unknown",
      };

      let printTestResult;
      if (forQr && urlImage) {
        printTestResult = await printQrCode(
          device,
          urlImage,
          doorLevel ?? null,
          liftLevel ?? null,
          companyName,
          address,
          qrHeader,
          qrSubText,
        );
      } else {
        printTestResult = await testPrint(device);
      }

      return {
        success: true,
        deviceInfo,
        printTest: printTestResult,
        message: printTestResult.success
          ? "Connection and print test successful"
          : "Connection successful but print test failed",
      };
    } catch (error: any) {
      return {
        success: false,
        error: error.message,
        message: "Connection failed",
      };
    } finally {
      if (device) {
        try {
          await device.close();
        } catch (closeError) {
          console.error("Error closing device:", closeError);
        }
      }
    }
  };

  const testPrint = async (device: USBDevice) => {
    try {
      const testCommands = [
        0x1b, 0x40,
        0x1b, 0x61, 0x01,
        0x1b, 0x21, 0x30,
        ...new TextEncoder().encode("TEST PRINT\n"),
        0x1b, 0x21, 0x00,
        ...new TextEncoder().encode("Printer connection test successful!\n"),
        ...new TextEncoder().encode("Date: " + new Date().toLocaleString() + "\n"),
        0x1d, 0x56, 0x00,
        0x0a, 0x0a, 0x0a, 0x0a,
      ];

      const testData = new Uint8Array(testCommands);
      const usbInterface = device.configuration.interfaces[0];
      const alternate = usbInterface.alternates[0];
      const outputEndpoint = alternate.endpoints.find(
        (ep: any) => ep.direction === "out",
      );

      if (!outputEndpoint) {
        throw new Error("No output endpoint found");
      }

      await device.transferOut(outputEndpoint.endpointNumber, testData);
      return { success: true, message: "Test print sent successfully" };
    } catch (error: any) {
      console.error("Test print error:", error);
      return { success: false, error: error.message, message: "Test print failed" };
    }
  };

  async function createReceiptLayout(
    qrUrlImage: string,
    doorLevel: number | string | null,
    liftLevel: number | string | null,
    companyName?: string,
    address?: string,
    qrHeader?: string,
    qrSubText?: string,
  ): Promise<HTMLCanvasElement> {
    const canvas = document.createElement("canvas");
    canvas.width = 576;
    canvas.height = 620;
    const ctx = canvas.getContext("2d")!;
    ctx.fillStyle = "#fff";
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = "#000";
    ctx.textAlign = "center";

    ctx.font = "bold 30px Arial";
    ctx.fillText(qrHeader || "Welcome", canvas.width / 2, 40);

    ctx.font = "20px Arial";
    ctx.fillText(
      qrSubText || "Proceed to Security Desk to check-out after visit",
      canvas.width / 2,
      70,
    );

    const qrSmall = 140;
    const qrCenter = 250;
    const margin = 20;
    const qrCanvas = await QRCode.toCanvas(qrUrlImage, { width: qrCenter });

    ctx.drawImage(qrCanvas, margin, 90, qrSmall, qrSmall);
    ctx.drawImage(qrCanvas, canvas.width - qrSmall - margin, 90, qrSmall, qrSmall);
    ctx.drawImage(qrCanvas, (canvas.width - qrCenter) / 2, 110, qrCenter, qrCenter);
    ctx.drawImage(qrCanvas, margin, 300, qrSmall, qrSmall);
    ctx.drawImage(qrCanvas, canvas.width - qrSmall - margin, 300, qrSmall, qrSmall);

    ctx.font = "25px Arial";
    ctx.fillText(companyName || "", canvas.width / 2, 470);

    ctx.font = "25px Arial";
    ctx.fillText(address || "", canvas.width / 2, 505);

    const now = new Date();
    const pad = (n: number) => n.toString().padStart(2, "0");
    const dateStr = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
    const timeStr = `${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;

    ctx.font = "20px Arial";
    ctx.fillText(`Date: ${dateStr} ${timeStr}`, canvas.width / 2, 540);

    let accessText = "Access: ";
    if (doorLevel && liftLevel) {
      accessText += "Door / Elevator";
    } else if (doorLevel) {
      accessText += "Door";
    } else if (liftLevel) {
      accessText += "Elevator";
    } else {
      accessText += "N/A";
    }
    ctx.fillText(accessText, canvas.width / 2, 570);

    ctx.font = "italic 20px Arial";
    let instructionText = "";
    if (doorLevel && liftLevel) {
      instructionText = `Scan ${doorLevel}. Scan and press Level ${liftLevel}. One time use.`;
    } else if (doorLevel) {
      instructionText = `Scan ${doorLevel}. One time use.`;
    } else if (liftLevel) {
      instructionText = `Scan and press Level ${liftLevel}. One time use.`;
    } else {
      instructionText = "One time use.";
    }
    ctx.fillText(instructionText, canvas.width / 2, 600);

    return canvas;
  }

  function canvasToRaster(canvas: HTMLCanvasElement): Uint8Array {
    const ctx = canvas.getContext("2d");
    if (!ctx) throw new Error("Canvas 2D context not available");

    const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    const pixels = imageData.data;
    const width = canvas.width;
    const height = canvas.height;
    const rowBytes = Math.ceil(width / 8);
    const bytes: number[] = [];

    bytes.push(0x1d, 0x76, 0x30, 0x00);
    bytes.push(rowBytes & 0xff, (rowBytes >> 8) & 0xff);
    bytes.push(height & 0xff, (height >> 8) & 0xff);

    for (let y = 0; y < height; y++) {
      for (let xByte = 0; xByte < rowBytes; xByte++) {
        let byte = 0;
        for (let bit = 0; bit < 8; bit++) {
          const x = xByte * 8 + bit;
          const idx = (y * width + x) * 4;
          if (x < width) {
            const r = pixels[idx];
            const g = pixels[idx + 1];
            const b = pixels[idx + 2];
            const avg = (r + g + b) / 3;
            if (avg < 128) {
              byte |= 0x80 >> bit;
            }
          }
        }
        bytes.push(byte);
      }
    }

    return new Uint8Array(bytes);
  }

  const printQrCode = async (
    device: USBDevice,
    urlImage: string,
    doorLevel: number | string | null,
    liftLevel: number | string | null,
    companyName?: string,
    address?: string,
    qrHeader?: string,
    qrSubText?: string,
  ) => {
    try {
      console.log("[WebUSB] printQrCode start — doorLevel:", doorLevel, "liftLevel:", liftLevel, "company:", companyName, "address:", address);
      const usbInterface = device.configuration.interfaces[0];
      const alternate = usbInterface.alternates[0];
      const outputEndpoint = alternate.endpoints.find(
        (ep: any) => ep.direction === "out",
      );
      if (!outputEndpoint) throw new Error("No output endpoint found");
      console.log("[WebUSB] Using endpoint:", outputEndpoint.endpointNumber);

      const canvas = await createReceiptLayout(
        urlImage,
        doorLevel,
        liftLevel,
        companyName,
        address,
        qrHeader,
        qrSubText,
      );
      console.log("[WebUSB] Canvas created:", canvas.width, "x", canvas.height);
      const rasterData = canvasToRaster(canvas);
      console.log("[WebUSB] Raster data size:", rasterData.byteLength, "bytes");

      await device.transferOut(outputEndpoint.endpointNumber, rasterData.buffer);
      console.log("[WebUSB] Raster data sent.");

      const CUT = new Uint8Array([0x1d, 0x56, 0x41, 0x10]);
      await device.transferOut(outputEndpoint.endpointNumber, CUT.buffer);
      console.log("[WebUSB] Cut command sent.");

      return { success: true, message: "QR print sent successfully" };
    } catch (error: any) {
      console.error("[WebUSB] printQrCode error:", error);
      return { success: false, error: error.message };
    }
  };

  return {
    isWebUsbSupported,
    getAvailableDevices,
    requestDevice,
    connectToDevice,
    testConnection,
  };
}
