import {
  ImageResult,
  PlatformService,
  PlatformCapabilities,
} from "../PlatformService";
import { logger } from "../../utils/logger";
import { QueryExecResult } from "@/interfaces/database";
import databaseService from "../AbsurdSqlDatabaseService";

/**
 * Platform service implementation for web browser platform.
 * Implements the PlatformService interface with web-specific functionality.
 *
 * @remarks
 * This service provides web-based implementations for:
 * - Image capture using the browser's file input
 * - Image selection from local filesystem
 * - Image processing and conversion
 *
 * Note: File system operations are not available in the web platform
 * due to browser security restrictions. These methods throw appropriate errors.
 */
export class WebPlatformService implements PlatformService {
  /**
   * Gets the capabilities of the web platform
   * @returns Platform capabilities object
   */
  getCapabilities(): PlatformCapabilities {
    return {
      hasFileSystem: false,
      hasCamera: true, // Through file input with capture
      isMobile: /iPhone|iPad|iPod|Android/i.test(navigator.userAgent),
      isIOS: /iPad|iPhone|iPod/.test(navigator.userAgent),
      hasFileDownload: true,
      needsFileHandlingInstructions: false,
    };
  }

  /**
   * Not supported in web platform.
   * @param _path - Unused path parameter
   * @throws Error indicating file system access is not available
   */
  async readFile(_path: string): Promise<string> {
    throw new Error("File system access not available in web platform");
  }

  /**
   * Not supported in web platform.
   * @param _path - Unused path parameter
   * @param _content - Unused content parameter
   * @throws Error indicating file system access is not available
   */
  async writeFile(_path: string, _content: string): Promise<void> {
    throw new Error("File system access not available in web platform");
  }

  /**
   * Not supported in web platform.
   * @param _path - Unused path parameter
   * @throws Error indicating file system access is not available
   */
  async deleteFile(_path: string): Promise<void> {
    throw new Error("File system access not available in web platform");
  }

  /**
   * Not supported in web platform.
   * @param _directory - Unused directory parameter
   * @throws Error indicating file system access is not available
   */
  async listFiles(_directory: string): Promise<string[]> {
    throw new Error("File system access not available in web platform");
  }

  /**
   * Opens the device camera for photo capture on desktop browsers using getUserMedia.
   * On mobile browsers, uses file input with capture attribute.
   * Falls back to file input if getUserMedia is not available or fails.
   *
   * @returns Promise resolving to the captured image data
   */
  async takePicture(): Promise<ImageResult> {
    const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
    const hasGetUserMedia = !!(
      navigator.mediaDevices && navigator.mediaDevices.getUserMedia
    );

    // If on mobile, use file input with capture attribute (existing behavior)
    if (isMobile || !hasGetUserMedia) {
      return new Promise((resolve, reject) => {
        const input = document.createElement("input");
        input.type = "file";
        input.accept = "image/*";
        input.capture = "environment";

        input.onchange = async (e) => {
          const file = (e.target as HTMLInputElement).files?.[0];
          if (file) {
            try {
              const blob = await this.processImageFile(file);
              resolve({
                blob,
                fileName: file.name || "photo.jpg",
              });
            } catch (error) {
              logger.error("Error processing camera image:", error);
              reject(new Error("Failed to process camera image"));
            }
          } else {
            reject(new Error("No image captured"));
          }
        };

        input.click();
      });
    }

    // Desktop: Use getUserMedia for webcam capture
    return new Promise((resolve, reject) => {
      let stream: MediaStream | null = null;
      let video: HTMLVideoElement | null = null;
      let captureButton: HTMLButtonElement | null = null;
      let overlay: HTMLDivElement | null = null;
      const cleanup = () => {
        if (stream) {
          stream.getTracks().forEach((track) => track.stop());
        }
        if (video && video.parentNode) video.parentNode.removeChild(video);
        if (captureButton && captureButton.parentNode)
          captureButton.parentNode.removeChild(captureButton);
        if (overlay && overlay.parentNode)
          overlay.parentNode.removeChild(overlay);
      };

      // Move async operations inside Promise body
      navigator.mediaDevices
        .getUserMedia({
          video: { facingMode: "user" },
        })
        .then((mediaStream) => {
          stream = mediaStream;
          // Create overlay for video and button
          overlay = document.createElement("div");
          overlay.style.position = "fixed";
          overlay.style.top = "0";
          overlay.style.left = "0";
          overlay.style.width = "100vw";
          overlay.style.height = "100vh";
          overlay.style.background = "rgba(0,0,0,0.8)";
          overlay.style.display = "flex";
          overlay.style.flexDirection = "column";
          overlay.style.justifyContent = "center";
          overlay.style.alignItems = "center";
          overlay.style.zIndex = "9999";

          video = document.createElement("video");
          video.autoplay = true;
          video.playsInline = true;
          video.style.maxWidth = "90vw";
          video.style.maxHeight = "70vh";
          video.srcObject = stream;
          overlay.appendChild(video);

          captureButton = document.createElement("button");
          captureButton.textContent = "Capture Photo";
          captureButton.style.marginTop = "2rem";
          captureButton.style.padding = "1rem 2rem";
          captureButton.style.fontSize = "1.2rem";
          captureButton.style.background = "#2563eb";
          captureButton.style.color = "white";
          captureButton.style.border = "none";
          captureButton.style.borderRadius = "0.5rem";
          captureButton.style.cursor = "pointer";
          overlay.appendChild(captureButton);

          document.body.appendChild(overlay);

          captureButton.onclick = () => {
            try {
              // Create a canvas to capture the frame
              const canvas = document.createElement("canvas");
              canvas.width = video!.videoWidth;
              canvas.height = video!.videoHeight;
              const ctx = canvas.getContext("2d");
              ctx?.drawImage(video!, 0, 0, canvas.width, canvas.height);
              canvas.toBlob(
                (blob) => {
                  cleanup();
                  if (blob) {
                    resolve({
                      blob,
                      fileName: `photo_${Date.now()}.jpg`,
                    });
                  } else {
                    reject(new Error("Failed to capture image from webcam"));
                  }
                },
                "image/jpeg",
                0.95,
              );
            } catch (err) {
              cleanup();
              reject(err);
            }
          };
        })
        .catch((error) => {
          cleanup();
          logger.error("Error accessing webcam:", error);
          // Fallback to file input
          const input = document.createElement("input");
          input.type = "file";
          input.accept = "image/*";
          input.onchange = (e) => {
            const file = (e.target as HTMLInputElement).files?.[0];
            if (file) {
              this.processImageFile(file)
                .then((blob) => {
                  resolve({
                    blob,
                    fileName: file.name || "photo.jpg",
                  });
                })
                .catch((error) => {
                  logger.error("Error processing fallback image:", error);
                  reject(new Error("Failed to process fallback image"));
                });
            } else {
              reject(new Error("No image selected"));
            }
          };
          input.click();
        });
    });
  }

  /**
   * Opens a file input dialog for selecting an image file.
   * Creates a temporary file input element to access local files.
   *
   * @returns Promise resolving to the selected image data
   * @throws Error if image processing fails or no image is selected
   *
   * @remarks
   * Allows selection of any image file type.
   * Processes the selected image to ensure consistent format.
   */
  async pickImage(): Promise<ImageResult> {
    return new Promise((resolve, reject) => {
      const input = document.createElement("input");
      input.type = "file";
      input.accept = "image/*";

      input.onchange = async (e) => {
        const file = (e.target as HTMLInputElement).files?.[0];
        if (file) {
          try {
            const blob = await this.processImageFile(file);
            resolve({
              blob,
              fileName: file.name || "photo.jpg",
            });
          } catch (error) {
            logger.error("Error processing picked image:", error);
            reject(new Error("Failed to process picked image"));
          }
        } else {
          reject(new Error("No image selected"));
        }
      };

      input.click();
    });
  }

  /**
   * Processes an image file to ensure consistent format.
   * Converts the file to a data URL and then to a Blob.
   *
   * @param file - The image File object to process
   * @returns Promise resolving to processed image Blob
   * @throws Error if file reading or conversion fails
   *
   * @remarks
   * This method ensures consistent image format across different
   * input sources by converting through data URL to Blob.
   */
  private async processImageFile(file: File): Promise<Blob> {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.onload = (event) => {
        const dataUrl = event.target?.result as string;
        // Convert to blob to ensure consistent format
        fetch(dataUrl)
          .then((res) => res.blob())
          .then((blob) => resolve(blob))
          .catch((error) => {
            logger.error("Error converting data URL to blob:", error);
            reject(error);
          });
      };
      reader.onerror = (error) => {
        logger.error("Error reading file:", error);
        reject(error);
      };
      reader.readAsDataURL(file);
    });
  }

  /**
   * Checks if running on Capacitor platform.
   * @returns false, as this is not Capacitor
   */
  isCapacitor(): boolean {
    return false;
  }

  /**
   * Checks if running on Electron platform.
   * @returns false, as this is not Electron
   */
  isElectron(): boolean {
    return false;
  }

  /**
   * Checks if running on PyWebView platform.
   * @returns false, as this is not PyWebView
   */
  isPyWebView(): boolean {
    return false;
  }

  /**
   * Checks if running on web platform.
   * @returns true, as this is the web implementation
   */
  isWeb(): boolean {
    return true;
  }

  /**
   * Handles deep link URLs in the web platform.
   * Deep links are handled through URL parameters in the web environment.
   *
   * @param _url - The deep link URL to handle (unused in web implementation)
   * @returns Promise that resolves immediately as web handles URLs naturally
   */
  async handleDeepLink(_url: string): Promise<void> {
    // Web platform can handle deep links through URL parameters
    return Promise.resolve();
  }

  /**
   * Not supported in web platform.
   * @param _fileName - Unused fileName parameter
   * @param _content - Unused content parameter
   * @throws Error indicating file system access is not available
   */
  async writeAndShareFile(_fileName: string, _content: string): Promise<void> {
    throw new Error("File system access not available in web platform");
  }

  /**
   * @see PlatformService.dbQuery
   */
  dbQuery(
    sql: string,
    params?: unknown[],
  ): Promise<QueryExecResult | undefined> {
    return databaseService.query(sql, params).then((result) => result[0]);
  }

  /**
   * @see PlatformService.dbExec
   */
  dbExec(
    sql: string,
    params?: unknown[],
  ): Promise<{ changes: number; lastId?: number }> {
    return databaseService.run(sql, params);
  }

  async dbGetOneRow(
    sql: string,
    params?: unknown[],
  ): Promise<unknown[] | undefined> {
    return databaseService
      .query(sql, params)
      .then((result: QueryExecResult[]) => result[0]?.values[0]);
  }
}
