import { Filesystem, Directory, Encoding } from "@capacitor/filesystem";
import {
  Camera,
  CameraResultType,
  CameraSource,
  CameraDirection,
} from "@capacitor/camera";
import { Capacitor } from "@capacitor/core";
import { Share } from "@capacitor/share";
import {
  SQLiteConnection,
  SQLiteDBConnection,
  CapacitorSQLite,
  DBSQLiteValues,
} from "@capacitor-community/sqlite";

import { runMigrations } from "@/db-sql/migration";
import { QueryExecResult } from "@/interfaces/database";
import {
  ImageResult,
  PlatformService,
  PlatformCapabilities,
} from "../PlatformService";
import { logger } from "../../utils/logger";
import { BaseDatabaseService } from "./BaseDatabaseService";

interface QueuedOperation {
  type: "run" | "query" | "rawQuery";
  sql: string;
  params: unknown[];
  resolve: (value: unknown) => void;
  reject: (reason: unknown) => void;
}

/**
 * Platform service implementation for Capacitor (mobile) platform.
 * Provides native mobile functionality through Capacitor plugins for:
 * - File system operations
 * - Camera and image picker
 * - Platform-specific features
 * - SQLite database operations
 */
export class CapacitorPlatformService
  extends BaseDatabaseService
  implements PlatformService
{
  /** Current camera direction */
  private currentDirection: CameraDirection = CameraDirection.Rear;

  private sqlite: SQLiteConnection;
  private db: SQLiteDBConnection | null = null;
  private dbName = "timesafari.sqlite";
  private initialized = false;
  private initializationPromise: Promise<void> | null = null;
  private operationQueue: Array<QueuedOperation> = [];
  private isProcessingQueue: boolean = false;

  constructor() {
    super();
    this.sqlite = new SQLiteConnection(CapacitorSQLite);
  }

  private async initializeDatabase(): Promise<void> {
    // If already initialized, return immediately
    if (this.initialized) {
      return;
    }

    // If initialization is in progress, wait for it
    if (this.initializationPromise) {
      return this.initializationPromise;
    }

    try {
      // Start initialization
      this.initializationPromise = this._initialize();
      await this.initializationPromise;
    } catch (error) {
      logger.error(
        "[CapacitorPlatformService] Initialize database method failed:",
        error,
      );
      this.initializationPromise = null; // Reset on failure
      throw error;
    }
  }

  private async _initialize(): Promise<void> {
    if (this.initialized) {
      return;
    }

    try {
      // Try to create/Open database connection
      try {
        this.db = await this.sqlite.createConnection(
          this.dbName,
          false,
          "no-encryption",
          1,
          false,
        );
      } catch (createError: unknown) {
        // If connection already exists, try to retrieve it or handle gracefully
        const errorMessage =
          createError instanceof Error
            ? createError.message
            : String(createError);
        const errorObj =
          typeof createError === "object" && createError !== null
            ? (createError as { errorMessage?: string; message?: string })
            : {};

        const fullErrorMessage =
          errorObj.errorMessage || errorObj.message || errorMessage;

        if (fullErrorMessage.includes("already exists")) {
          logger.debug(
            "[CapacitorPlatformService] Connection already exists on native side, attempting to retrieve",
          );
          // Check if connection exists in JavaScript Map
          const isConnResult = await this.sqlite.isConnection(
            this.dbName,
            false,
          );
          if (isConnResult.result) {
            // Connection exists in Map, retrieve it
            this.db = await this.sqlite.retrieveConnection(this.dbName, false);
            logger.debug(
              "[CapacitorPlatformService] Successfully retrieved existing connection from Map",
            );
          } else {
            // Connection exists on native side but not in JavaScript Map
            // This can happen when the app is restarted but native connections persist
            // Try to close the native connection first, then create a new one
            logger.debug(
              "[CapacitorPlatformService] Connection exists natively but not in Map, closing and recreating",
            );
            try {
              await this.sqlite.closeConnection(this.dbName, false);
            } catch (closeError) {
              // Ignore close errors - connection might not be properly tracked
              logger.debug(
                "[CapacitorPlatformService] Error closing connection (may be expected):",
                closeError,
              );
            }
            // Now try to create the connection again
            this.db = await this.sqlite.createConnection(
              this.dbName,
              false,
              "no-encryption",
              1,
              false,
            );
            logger.debug(
              "[CapacitorPlatformService] Successfully created connection after cleanup",
            );
          }
        } else {
          // Re-throw if it's a different error
          throw createError;
        }
      }

      // Open the connection if it's not already open
      try {
        await this.db.open();
      } catch (openError: unknown) {
        const openErrorMessage =
          openError instanceof Error ? openError.message : String(openError);
        // If already open, that's fine - continue
        if (!openErrorMessage.includes("already open")) {
          throw openError;
        }
        logger.debug(
          "[CapacitorPlatformService] Database connection already open",
        );
      }

      // Set journal mode to WAL for better performance
      // await this.db.execute("PRAGMA journal_mode=WAL;");

      // Run migrations
      await this.runCapacitorMigrations();

      this.initialized = true;
      logger.log(
        "[CapacitorPlatformService] SQLite database initialized successfully",
      );

      // Start processing the queue after initialization
      this.processQueue();
    } catch (error) {
      logger.error(
        "[CapacitorPlatformService] Error initializing SQLite database:",
        error,
      );
      throw new Error(
        "[CapacitorPlatformService] Failed to initialize database",
      );
    }
  }

  private async processQueue(): Promise<void> {
    if (this.isProcessingQueue || !this.initialized || !this.db) {
      return;
    }

    this.isProcessingQueue = true;

    while (this.operationQueue.length > 0) {
      const operation = this.operationQueue.shift();
      if (!operation) continue;

      try {
        let result: unknown;
        switch (operation.type) {
          case "run": {
            const runResult = await this.db.run(
              operation.sql,
              operation.params,
            );
            result = {
              changes: runResult.changes?.changes || 0,
              lastId: runResult.changes?.lastId,
            };
            break;
          }
          case "query": {
            const queryResult = await this.db.query(
              operation.sql,
              operation.params,
            );
            result = {
              columns: Object.keys(queryResult.values?.[0] || {}),
              values: (queryResult.values || []).map((row) =>
                Object.values(row),
              ),
            };
            break;
          }
          case "rawQuery": {
            const queryResult = await this.db.query(
              operation.sql,
              operation.params,
            );
            result = queryResult;
            break;
          }
        }
        operation.resolve(result);
      } catch (error) {
        logger.error(
          "[CapacitorPlatformService] Error while processing SQL queue:",
          error,
        );
        logger.error(
          `[CapacitorPlatformService] Failed operation - Type: ${operation.type}, SQL: ${operation.sql}`,
        );
        logger.error(
          `[CapacitorPlatformService] Failed operation - Params:`,
          operation.params,
        );
        operation.reject(error);
      }
    }

    this.isProcessingQueue = false;
  }

  private async queueOperation<R>(
    type: QueuedOperation["type"],
    sql: string,
    params: unknown[] = [],
  ): Promise<R> {
    // Only log SQL operations in debug mode to reduce console noise
    logger.debug(`[CapacitorPlatformService] queueOperation - SQL: ${sql}`);

    // Convert parameters to SQLite-compatible types with robust serialization
    const convertedParams = params.map((param, index) => {
      if (param === null || param === undefined) {
        return null;
      }
      if (typeof param === "object" && param !== null) {
        // Special handling for Proxy objects (common cause of "An object could not be cloned")
        const isProxy = this.isProxyObject(param);

        // AGGRESSIVE: If toString contains "Proxy", treat as Proxy even if isProxyObject returns false
        const stringRep = String(param);
        const forceProxyDetection =
          stringRep.includes("Proxy(") || stringRep.startsWith("Proxy");

        if (isProxy || forceProxyDetection) {
          logger.debug(
            `[CapacitorPlatformService] Proxy object detected at index ${index}`,
          );
          try {
            // AGGRESSIVE EXTRACTION: Try multiple methods to extract actual values
            if (Array.isArray(param)) {
              // Method 1: Array.from() to extract from Proxy(Array)
              const actualArray = Array.from(param);
              return actualArray;
            } else {
              // For Proxy(Object), try to extract actual object
              const actualObject = Object.assign({}, param);
              return actualObject;
            }
          } catch (proxyError) {
            logger.debug(
              `[CapacitorPlatformService] Failed to extract from Proxy at index ${index}:`,
              proxyError,
            );

            // FALLBACK: Try to extract primitive values manually
            if (Array.isArray(param)) {
              try {
                const fallbackArray: unknown[] = [];
                for (let i = 0; i < param.length; i++) {
                  fallbackArray.push(param[i]);
                }
                return fallbackArray;
              } catch (fallbackError) {
                return `[Proxy Array - Could not extract]`;
              }
            }
            return `[Proxy Object - Could not extract]`;
          }
        }

        try {
          // Safely convert objects and arrays to JSON strings
          return JSON.stringify(param);
        } catch (error) {
          // Handle non-serializable objects
          logger.debug(
            `[CapacitorPlatformService] Failed to serialize parameter at index ${index}:`,
            error,
          );

          // Fallback: Convert to string representation
          if (Array.isArray(param)) {
            return `[Array(${param.length})]`;
          }
          return `[Object ${param.constructor?.name || "Unknown"}]`;
        }
      }
      if (typeof param === "boolean") {
        // Convert boolean to integer (0 or 1)
        return param ? 1 : 0;
      }
      if (typeof param === "function") {
        // Functions can't be serialized - convert to string representation
        logger.debug(
          `[CapacitorPlatformService] Function parameter detected and converted to string at index ${index}`,
        );
        return `[Function ${param.name || "Anonymous"}]`;
      }
      if (typeof param === "symbol") {
        // Symbols can't be serialized - convert to string representation
        logger.debug(
          `[CapacitorPlatformService] Symbol parameter detected and converted to string at index ${index}`,
        );
        return param.toString();
      }
      // Numbers, strings, bigints are supported, but ensure bigints are converted to strings
      if (typeof param === "bigint") {
        return param.toString();
      }
      return param;
    });

    return new Promise<R>((resolve, reject) => {
      // Create completely plain objects that Vue cannot make reactive
      // Step 1: Deep clone the converted params to ensure they're plain objects
      const plainParams = JSON.parse(JSON.stringify(convertedParams));

      // Step 2: Create operation object using Object.create(null) for no prototype
      const operation = Object.create(null) as QueuedOperation;
      operation.type = type;
      operation.sql = sql;
      operation.params = plainParams;
      operation.resolve = (value: unknown) => resolve(value as R);
      operation.reject = reject;

      // Step 3: Freeze everything to prevent modification
      Object.freeze(operation.params);
      Object.freeze(operation);

      this.operationQueue.push(operation);

      // If we're already initialized, start processing the queue
      if (this.initialized && this.db) {
        this.processQueue();
      }
    });
  }

  private async waitForInitialization(): Promise<void> {
    // If we have an initialization promise, wait for it
    if (this.initializationPromise) {
      await this.initializationPromise;
      return;
    }

    // If not initialized and no promise, start initialization
    if (!this.initialized) {
      await this.initializeDatabase();
      return;
    }

    // If initialized but no db, something went wrong
    if (!this.db) {
      logger.error(
        "[CapacitorPlatformService] Database not properly initialized after await waitForInitialization() - initialized flag is true but db is null",
      );
      throw new Error(
        "[CapacitorPlatformService] The database could not be initialized. We recommend you restart or reinstall.",
      );
    }
  }

  /**
   * Detect if an object is a Proxy object that cannot be serialized
   * Proxy objects cause "An object could not be cloned" errors in Capacitor
   * @param obj - Object to test
   * @returns true if the object appears to be a Proxy
   */
  private isProxyObject(obj: unknown): boolean {
    if (typeof obj !== "object" || obj === null) {
      return false;
    }

    try {
      // Method 1: Check toString representation
      const objString = obj.toString();
      if (objString.includes("Proxy(") || objString.startsWith("Proxy")) {
        logger.debug(
          "[CapacitorPlatformService] Proxy detected via toString:",
          objString,
        );
        return true;
      }

      // Method 2: Check constructor name
      const constructorName = obj.constructor?.name;
      if (constructorName === "Proxy") {
        logger.debug(
          "[CapacitorPlatformService] Proxy detected via constructor name",
        );
        return true;
      }

      // Method 3: Check Object.prototype.toString
      const objToString = Object.prototype.toString.call(obj);
      if (objToString.includes("Proxy")) {
        logger.debug(
          "[CapacitorPlatformService] Proxy detected via Object.prototype.toString",
        );
        return true;
      }

      // Method 4: Vue/Reactive Proxy detection - check for __v_ properties
      if (typeof obj === "object" && obj !== null) {
        // Check for Vue reactive proxy indicators
        const hasVueProxy = Object.getOwnPropertyNames(obj).some(
          (prop) => prop.startsWith("__v_") || prop.startsWith("__r_"),
        );
        if (hasVueProxy) {
          logger.debug(
            "[CapacitorPlatformService] Vue reactive Proxy detected",
          );
          return true;
        }
      }

      // Method 5: Try JSON.stringify and check for Proxy in error or result
      try {
        const jsonString = JSON.stringify(obj);
        if (jsonString.includes("Proxy")) {
          logger.debug(
            "[CapacitorPlatformService] Proxy detected in JSON serialization",
          );
          return true;
        }
      } catch (jsonError) {
        // If JSON.stringify fails, it might be a non-serializable Proxy
        const errorMessage =
          jsonError instanceof Error ? jsonError.message : String(jsonError);
        if (
          errorMessage.includes("Proxy") ||
          errorMessage.includes("circular") ||
          errorMessage.includes("clone")
        ) {
          logger.debug(
            "[CapacitorPlatformService] Proxy detected via JSON serialization error",
          );
          return true;
        }
      }

      return false;
    } catch (error) {
      // If we can't inspect the object, it might be a Proxy causing issues
      logger.warn(
        "[CapacitorPlatformService] Could not inspect object for Proxy detection:",
        error,
      );
      return true; // Assume it's a Proxy if we can't inspect it
    }
  }

  /**
   * Execute database migrations for the Capacitor platform
   *
   * This method orchestrates the database migration process specifically for
   * Capacitor-based platforms (mobile and Electron). It provides the platform-specific
   * SQL execution functions to the migration service and handles Capacitor SQLite
   * plugin integration.
   *
   * ## Migration Process:
   *
   * 1. **SQL Execution Setup**: Creates platform-specific SQL execution functions
   *    that properly handle the Capacitor SQLite plugin's API
   *
   * 2. **Parameter Handling**: Ensures proper parameter binding for prepared statements
   *    using the correct Capacitor SQLite methods (run vs execute)
   *
   * 3. **Result Parsing**: Provides extraction functions that understand the
   *    Capacitor SQLite result format
   *
   * 4. **Migration Execution**: Delegates to the migration service for the actual
   *    migration logic and tracking
   *
   * 5. **Integrity Verification**: Runs post-migration integrity checks to ensure
   *    the database is in the expected state
   *
   * ## Error Handling:
   *
   * The method includes comprehensive error handling for:
   * - Database connection issues
   * - SQL execution failures
   * - Migration tracking problems
   * - Schema validation errors
   *
   * Even if migrations fail, the integrity check still runs to assess the
   * current database state and provide debugging information.
   *
   * ## Logging:
   *
   * Detailed logging is provided throughout the process using emoji-tagged
   * console messages that appear in the Electron DevTools console. This
   * includes:
   * - SQL statement execution details
   * - Parameter values for debugging
   * - Migration success/failure status
   * - Database integrity check results
   *
   * @throws {Error} If database is not initialized or migrations fail critically
   * @private Internal method called during database initialization
   *
   * @example
   * ```typescript
   * // Called automatically during platform service initialization
   * await this.runCapacitorMigrations();
   * ```
   */
  private async runCapacitorMigrations(): Promise<void> {
    if (!this.db) {
      throw new Error("Database not initialized");
    }

    /**
     * SQL execution function for Capacitor SQLite plugin
     *
     * This function handles the execution of SQL statements (INSERT, UPDATE, CREATE, etc.)
     * through the Capacitor SQLite plugin. It automatically chooses the appropriate
     * method based on whether parameters are provided.
     *
     * @param sql - SQL statement to execute
     * @param params - Optional parameters for prepared statements
     * @returns Promise resolving to execution results
     */
    const sqlExec = async (sql: string, params?: unknown[]): Promise<void> => {
      logger.debug(`🔧 [CapacitorMigration] Executing SQL:`, sql);

      if (params && params.length > 0) {
        // Use run method for parameterized queries (prepared statements)
        // This is essential for proper parameter binding and SQL injection prevention
        await this.db!.run(sql, params);
      } else {
        // For multi-statement SQL (like migrations), use executeSet method
        // This handles multiple statements properly
        if (
          sql.includes(";") &&
          sql.split(";").filter((s) => s.trim()).length > 1
        ) {
          // Multi-statement SQL - use executeSet for proper handling
          const statements = sql.split(";").filter((s) => s.trim());
          await this.db!.executeSet(
            statements.map((stmt) => ({
              statement: stmt.trim(),
              values: [], // Empty values array for non-parameterized statements
            })),
          );
        } else {
          // Single statement - use execute method
          await this.db!.execute(sql);
        }
      }
    };

    /**
     * SQL query function for Capacitor SQLite plugin
     *
     * This function handles the execution of SQL queries (SELECT statements)
     * through the Capacitor SQLite plugin. It returns the raw result data
     * that can be processed by the migration service.
     *
     * @param sql - SQL query to execute
     * @param params - Optional parameters for prepared statements
     * @returns Promise resolving to query results
     */
    const sqlQuery = async (
      sql: string,
      params?: unknown[],
    ): Promise<DBSQLiteValues> => {
      logger.debug(`🔍 [CapacitorMigration] Querying SQL:`, sql);

      const result = await this.db!.query(sql, params);
      return result;
    };

    /**
     * Extract migration names from Capacitor SQLite query results
     *
     * This function parses the result format returned by the Capacitor SQLite
     * plugin and extracts migration names. It handles the specific data structure
     * used by the plugin, which can vary between different result formats.
     *
     * ## Result Format Handling:
     *
     * The Capacitor SQLite plugin can return results in different formats:
     * - Object format: `{ name: "migration_name" }`
     * - Array format: `["migration_name", "timestamp"]`
     *
     * This function handles both formats to ensure robust migration name extraction.
     *
     * @param result - Query result from Capacitor SQLite plugin
     * @returns Set of migration names found in the result
     */
    const extractMigrationNames = (result: DBSQLiteValues): Set<string> => {
      logger.debug(
        `🔍 [CapacitorMigration] Extracting migration names from:`,
        result,
      );

      // Handle the Capacitor SQLite result format
      const names =
        result.values
          ?.map((row: unknown) => {
            // The row could be an object with 'name' property or an array where name is first element
            if (typeof row === "object" && row !== null && "name" in row) {
              return (row as { name: string }).name;
            } else if (Array.isArray(row) && row.length > 0) {
              return row[0];
            }
            return null;
          })
          .filter((name) => name !== null) || [];

      logger.debug(`📋 [CapacitorMigration] Extracted names:`, names);
      return new Set(names);
    };

    try {
      // Execute the migration process
      await runMigrations(sqlExec, sqlQuery, extractMigrationNames);

      // After migrations, run integrity check to verify database state
      await this.verifyDatabaseIntegrity();
    } catch (error) {
      logger.error(`❌ [CapacitorMigration] Migration failed:`, error);
      // Still try to verify what we have for debugging purposes
      await this.verifyDatabaseIntegrity();
      throw error;
    }
  }

  /**
   * Verify database integrity and migration status
   *
   * This method performs comprehensive validation of the database structure
   * and migration state. It's designed to help identify issues with the
   * migration process and provide detailed debugging information.
   *
   * ## Validation Steps:
   *
   * 1. **Migration Records**: Checks which migrations are recorded as applied
   * 2. **Table Existence**: Verifies all expected core tables exist
   * 3. **Schema Validation**: Checks table schemas including column presence
   * 4. **Data Integrity**: Validates basic data counts and structure
   *
   * ## Core Tables Validated:
   *
   * - `accounts`: User identity and cryptographic keys
   * - `secret`: Application secrets and encryption keys
   * - `settings`: Configuration and user preferences
   * - `contacts`: Contact network and trust relationships
   * - `logs`: Application event logging
   * - `temp`: Temporary data storage
   *
   * ## Schema Checks:
   *
   * For critical tables like `contacts`, the method validates:
   * - Table structure using `PRAGMA table_info`
   * - Presence of important columns (e.g., `iViewContent`)
   * - Column data types and constraints
   *
   * ## Error Handling:
   *
   * This method is designed to never throw errors - it captures and logs
   * all validation issues for debugging purposes. This ensures that even
   * if integrity checks fail, they don't prevent the application from starting.
   *
   * ## Logging Output:
   *
   * The method produces detailed console output with emoji tags:
   * - `✅` for successful validations
   * - `❌` for validation failures
   * - `📊` for data summaries
   * - `🔍` for investigation steps
   *
   * @private Internal method called after migrations
   *
   * @example
   * ```typescript
   * // Called automatically after migration completion
   * await this.verifyDatabaseIntegrity();
   * ```
   */
  private async verifyDatabaseIntegrity(): Promise<void> {
    if (!this.db) {
      logger.error(`❌ [DB-Integrity] Database not initialized`);
      return;
    }

    logger.debug(`🔍 [DB-Integrity] Starting database integrity check...`);

    try {
      // Step 1: Check migrations table and applied migrations
      const migrationsResult = await this.db.query(
        "SELECT name, applied_at FROM migrations ORDER BY applied_at",
      );
      logger.debug(`📊 [DB-Integrity] Applied migrations:`, migrationsResult);

      // Step 2: Verify core tables exist
      const coreTableNames = [
        "accounts",
        "secret",
        "settings",
        "contacts",
        "logs",
        "temp",
      ];
      const existingTables: string[] = [];

      for (const tableName of coreTableNames) {
        try {
          const tableCheck = await this.db.query(
            `SELECT name FROM sqlite_master WHERE type='table' AND name='${tableName}'`,
          );
          if (tableCheck.values && tableCheck.values.length > 0) {
            existingTables.push(tableName);
            logger.debug(`✅ [DB-Integrity] Table ${tableName} exists`);
          } else {
            logger.error(`❌ [DB-Integrity] Table ${tableName} missing`);
          }
        } catch (error) {
          logger.error(
            `❌ [DB-Integrity] Error checking table ${tableName}:`,
            error,
          );
        }
      }

      // Step 3: Check contacts table schema (including iViewContent column)
      if (existingTables.includes("contacts")) {
        try {
          const contactsSchema = await this.db.query(
            "PRAGMA table_info(contacts)",
          );
          logger.debug(
            `📊 [DB-Integrity] Contacts table schema:`,
            contactsSchema,
          );

          // Check for iViewContent column specifically
          const hasIViewContent = contactsSchema.values?.some(
            (col: unknown) =>
              (typeof col === "object" &&
                col !== null &&
                "name" in col &&
                (col as { name: string }).name === "iViewContent") ||
              (Array.isArray(col) && col[1] === "iViewContent"),
          );

          if (hasIViewContent) {
            logger.debug(
              `✅ [DB-Integrity] iViewContent column exists in contacts table`,
            );
          } else {
            logger.error(
              `❌ [DB-Integrity] iViewContent column missing from contacts table`,
            );
          }
        } catch (error) {
          logger.error(
            `❌ [DB-Integrity] Error checking contacts schema:`,
            error,
          );
        }
      }

      // Step 4: Check for basic data integrity
      try {
        const accountCount = await this.db.query(
          "SELECT COUNT(*) as count FROM accounts",
        );
        const settingsCount = await this.db.query(
          "SELECT COUNT(*) as count FROM settings",
        );
        const contactsCount = await this.db.query(
          "SELECT COUNT(*) as count FROM contacts",
        );

        logger.debug(
          `📊 [DB-Integrity] Data counts - Accounts: ${JSON.stringify(accountCount)}, Settings: ${JSON.stringify(settingsCount)}, Contacts: ${JSON.stringify(contactsCount)}`,
        );
      } catch (error) {
        logger.error(`❌ [DB-Integrity] Error checking data counts:`, error);
      }

      logger.log(`✅ [DB-Integrity] Database integrity check completed`);
    } catch (error) {
      logger.error(`❌ [DB-Integrity] Database integrity check failed:`, error);
    }
  }

  /**
   * Gets the capabilities of the Capacitor platform
   * @returns Platform capabilities object
   */
  getCapabilities(): PlatformCapabilities {
    const platform = Capacitor.getPlatform();

    return {
      hasFileSystem: true,
      hasCamera: true,
      isMobile: true, // Capacitor is always mobile
      isIOS: platform === "ios",
      hasFileDownload: false, // Mobile platforms need sharing
      needsFileHandlingInstructions: true, // Mobile needs instructions
      isNativeApp: true,
    };
  }

  /**
   * Checks and requests storage permissions if needed
   * @returns Promise that resolves when permissions are granted
   * @throws Error if permissions are denied
   */
  private async checkStoragePermissions(): Promise<void> {
    try {
      const logData = {
        platform: this.getCapabilities().isIOS ? "iOS" : "Android",
        timestamp: new Date().toISOString(),
      };
      logger.log(
        "Checking storage permissions",
        JSON.stringify(logData, null, 2),
      );

      if (this.getCapabilities().isIOS) {
        // iOS uses different permission model
        return;
      }

      // Try to access a test directory to check permissions
      try {
        await Filesystem.stat({
          path: "/storage/emulated/0/Download",
          directory: Directory.Documents,
        });
        logger.log(
          "Storage permissions already granted",
          JSON.stringify({ timestamp: new Date().toISOString() }, null, 2),
        );
        return;
      } catch (error: unknown) {
        const err = error as Error;
        const errorLogData = {
          error: {
            message: err.message,
            name: err.name,
            stack: err.stack,
          },
          timestamp: new Date().toISOString(),
        };

        // "File does not exist" is expected and not a permission error
        if (err.message === "File does not exist") {
          logger.log(
            "Directory does not exist (expected), proceeding with write",
            JSON.stringify(errorLogData, null, 2),
          );
          return;
        }

        // Check for actual permission errors
        if (
          err.message.includes("permission") ||
          err.message.includes("access")
        ) {
          logger.log(
            "Permission check failed, requesting permissions",
            JSON.stringify(errorLogData, null, 2),
          );

          // The Filesystem plugin will automatically request permissions when needed
          // We just need to try the operation again
          try {
            await Filesystem.stat({
              path: "/storage/emulated/0/Download",
              directory: Directory.Documents,
            });
            logger.log(
              "Storage permissions granted after request",
              JSON.stringify({ timestamp: new Date().toISOString() }, null, 2),
            );
            return;
          } catch (retryError: unknown) {
            const retryErr = retryError as Error;
            throw new Error(
              `Failed to obtain storage permissions: ${retryErr.message}`,
            );
          }
        }

        // For any other error, log it but don't treat as permission error
        logger.log(
          "Unexpected error during permission check",
          JSON.stringify(errorLogData, null, 2),
        );
        return;
      }
    } catch (error: unknown) {
      const err = error as Error;
      const errorLogData = {
        error: {
          message: err.message,
          name: err.name,
          stack: err.stack,
        },
        timestamp: new Date().toISOString(),
      };
      logger.error(
        "Error checking/requesting permissions",
        JSON.stringify(errorLogData, null, 2),
      );
      throw new Error(`Failed to obtain storage permissions: ${err.message}`);
    }
  }

  /**
   * Reads a file from the app's data directory.
   * @param path - Relative path to the file in the app's data directory
   * @returns Promise resolving to the file contents as string
   * @throws Error if file cannot be read or doesn't exist
   */
  async readFile(path: string): Promise<string> {
    const file = await Filesystem.readFile({
      path,
      directory: Directory.Data,
    });
    if (file.data instanceof Blob) {
      return await file.data.text();
    }
    return file.data;
  }

  /**
   * Writes content to a file in the app's safe storage and offers sharing.
   *
   * Platform-specific behavior:
   * - Saves to app's Documents directory
   * - Offers sharing functionality to move file elsewhere
   *
   * The method handles:
   * 1. Writing to app-safe storage
   * 2. Sharing the file with user's preferred app
   * 3. Error handling and logging
   *
   * @param fileName - The name of the file to create (e.g. "backup.json")
   * @param content - The content to write to the file
   *
   * @throws Error if:
   * - File writing fails
   * - Sharing fails
   *
   * @example
   * ```typescript
   * // Save and share a JSON file
   * await platformService.writeFile(
   *   "backup.json",
   *   JSON.stringify(data)
   * );
   * ```
   */
  async writeFile(fileName: string, content: string): Promise<void> {
    try {
      // Check storage permissions before proceeding
      await this.checkStoragePermissions();

      const logData = {
        targetFileName: fileName,
        contentLength: content.length,
        platform: this.getCapabilities().isIOS ? "iOS" : "Android",
        timestamp: new Date().toISOString(),
      };
      logger.log(
        "Starting writeFile operation",
        JSON.stringify(logData, null, 2),
      );

      // For Android, we need to handle content URIs differently
      if (this.getCapabilities().isIOS) {
        // Write to app's Documents directory for iOS
        const writeResult = await Filesystem.writeFile({
          path: fileName,
          data: content,
          directory: Directory.Data,
          encoding: Encoding.UTF8,
        });

        const writeSuccessLogData = {
          path: writeResult.uri,
          timestamp: new Date().toISOString(),
        };
        logger.log(
          "File write successful",
          JSON.stringify(writeSuccessLogData, null, 2),
        );

        // Offer to share the file
        try {
          await Share.share({
            title: "TimeSafari Backup",
            text: "Here is your TimeSafari backup file.",
            url: writeResult.uri,
            dialogTitle: "Share your backup",
          });

          logger.log(
            "Share dialog shown",
            JSON.stringify({ timestamp: new Date().toISOString() }, null, 2),
          );
        } catch (shareError) {
          // Log share error but don't fail the operation
          logger.error(
            "Share dialog failed",
            JSON.stringify(
              {
                error: shareError,
                timestamp: new Date().toISOString(),
              },
              null,
              2,
            ),
          );
        }
      } else {
        // For Android, first write to app's Documents directory
        const writeResult = await Filesystem.writeFile({
          path: fileName,
          data: content,
          directory: Directory.Data,
          encoding: Encoding.UTF8,
        });

        const writeSuccessLogData = {
          path: writeResult.uri,
          timestamp: new Date().toISOString(),
        };
        logger.log(
          "File write successful to app storage",
          JSON.stringify(writeSuccessLogData, null, 2),
        );

        // Then share the file to let user choose where to save it
        try {
          await Share.share({
            title: "TimeSafari Backup",
            text: "Here is your TimeSafari backup file.",
            url: writeResult.uri,
            dialogTitle: "Save your backup",
          });

          logger.log(
            "Share dialog shown for Android",
            JSON.stringify({ timestamp: new Date().toISOString() }, null, 2),
          );
        } catch (shareError) {
          // Log share error but don't fail the operation
          logger.error(
            "Share dialog failed for Android",
            JSON.stringify(
              {
                error: shareError,
                timestamp: new Date().toISOString(),
              },
              null,
              2,
            ),
          );
        }
      }
    } catch (error: unknown) {
      const err = error as Error;
      const finalErrorLogData = {
        error: {
          message: err.message,
          name: err.name,
          stack: err.stack,
        },
        timestamp: new Date().toISOString(),
      };
      logger.error(
        "Error in writeFile operation:",
        JSON.stringify(finalErrorLogData, null, 2),
      );
      throw new Error(`Failed to save file: ${err.message}`);
    }
  }

  /**
   * Writes content to a file in the device's app-private storage.
   * Then shares the file using the system share dialog.
   *
   * Works on both Android and iOS without needing external storage permissions.
   *
   * @param fileName - The name of the file to create (e.g. "backup.json")
   * @param content - The content to write to the file
   */
  async writeAndShareFile(fileName: string, content: string): Promise<void> {
    const timestamp = new Date().toISOString();
    const logData = {
      action: "writeAndShareFile",
      fileName,
      contentLength: content.length,
      timestamp,
    };
    logger.log("[CapacitorPlatformService]", JSON.stringify(logData, null, 2));

    try {
      // Check storage permissions before proceeding
      await this.checkStoragePermissions();

      const { uri } = await Filesystem.writeFile({
        path: fileName,
        data: content,
        directory: Directory.Data,
        encoding: Encoding.UTF8,
        recursive: true,
      });

      logger.log("[CapacitorPlatformService] File write successful:", {
        uri,
        timestamp: new Date().toISOString(),
      });

      await Share.share({
        title: "TimeSafari Backup",
        text: "Here is your backup file.",
        url: uri,
        dialogTitle: "Share your backup file",
      });
    } catch (error) {
      const err = error as Error;
      const errLog = {
        message: err.message,
        stack: err.stack,
        timestamp: new Date().toISOString(),
      };
      logger.error(
        "[CapacitorPlatformService] Error writing or sharing file:",
        JSON.stringify(errLog, null, 2),
      );
      throw new Error(`Failed to write or share file: ${err.message}`);
    }
  }

  /**
   * Deletes a file from the app's data directory.
   * @param path - Relative path to the file to delete
   * @throws Error if deletion fails or file doesn't exist
   */
  async deleteFile(path: string): Promise<void> {
    await Filesystem.deleteFile({
      path,
      directory: Directory.Data,
    });
  }

  /**
   * Lists files in the specified directory within app's data directory.
   * @param directory - Relative path to the directory to list
   * @returns Promise resolving to array of filenames
   * @throws Error if directory cannot be read or doesn't exist
   */
  async listFiles(directory: string): Promise<string[]> {
    const result = await Filesystem.readdir({
      path: directory,
      directory: Directory.Data,
    });
    return result.files.map((file) =>
      typeof file === "string" ? file : file.name,
    );
  }

  /**
   * Opens the device camera to take a picture.
   * Configures camera for high quality images with editing enabled.
   * @returns Promise resolving to the captured image data
   * @throws Error if camera access fails or user cancels
   */
  async takePicture(): Promise<ImageResult> {
    try {
      const image = await Camera.getPhoto({
        quality: 90,
        allowEditing: true,
        resultType: CameraResultType.Base64,
        source: CameraSource.Camera,
        direction: this.currentDirection,
      });

      const blob = await this.processImageData(image.base64String);
      return {
        blob,
        fileName: `photo_${Date.now()}.${image.format || "jpg"}`,
      };
    } catch (error) {
      logger.error("Error taking picture with Capacitor:", error);
      throw new Error("Failed to take picture");
    }
  }

  /**
   * Opens the device photo gallery to pick an existing image.
   * Configures picker for high quality images with editing enabled.
   * @returns Promise resolving to the selected image data
   * @throws Error if gallery access fails or user cancels
   */
  async pickImage(): Promise<ImageResult> {
    try {
      const image = await Camera.getPhoto({
        quality: 90,
        allowEditing: true,
        resultType: CameraResultType.Base64,
        source: CameraSource.Photos,
      });

      const blob = await this.processImageData(image.base64String);
      return {
        blob,
        fileName: `photo_${Date.now()}.${image.format || "jpg"}`,
      };
    } catch (error) {
      logger.error("Error picking image with Capacitor:", error);
      throw new Error("Failed to pick image");
    }
  }

  /**
   * Converts base64 image data to a Blob.
   * @param base64String - Base64 encoded image data
   * @returns Promise resolving to image Blob
   * @throws Error if conversion fails
   */
  private async processImageData(base64String?: string): Promise<Blob> {
    if (!base64String) {
      throw new Error("No image data received");
    }

    // Convert base64 to blob
    const byteCharacters = atob(base64String);
    const byteArrays = [];
    for (let offset = 0; offset < byteCharacters.length; offset += 512) {
      const slice = byteCharacters.slice(offset, offset + 512);
      const byteNumbers = new Array(slice.length);
      for (let i = 0; i < slice.length; i++) {
        byteNumbers[i] = slice.charCodeAt(i);
      }
      const byteArray = new Uint8Array(byteNumbers);
      byteArrays.push(byteArray);
    }
    return new Blob(byteArrays, { type: "image/jpeg" });
  }

  /**
   * Rotates the camera between front and back cameras.
   * @returns Promise that resolves when the camera is rotated
   */
  async rotateCamera(): Promise<void> {
    this.currentDirection =
      this.currentDirection === CameraDirection.Rear
        ? CameraDirection.Front
        : CameraDirection.Rear;
    logger.debug(`Camera rotated to ${this.currentDirection} camera`);
  }

  /**
   * Handles deep link URLs for the application.
   * Note: Capacitor handles deep links automatically.
   * @param _url - The deep link URL (unused)
   */
  async handleDeepLink(_url: string): Promise<void> {
    // Capacitor handles deep links automatically
    // This is just a placeholder for the interface
    return Promise.resolve();
  }

  /**
   * @see PlatformService.dbQuery
   */
  async dbQuery(sql: string, params?: unknown[]): Promise<QueryExecResult> {
    await this.waitForInitialization();
    return this.queueOperation<QueryExecResult>("query", sql, params || []);
  }

  /**
   * @see PlatformService.dbExec
   */
  async dbExec(
    sql: string,
    params?: unknown[],
  ): Promise<{ changes: number; lastId?: number }> {
    await this.waitForInitialization();
    return this.queueOperation<{ changes: number; lastId?: number }>(
      "run",
      sql,
      params || [],
    );
  }

  /**
   * @see PlatformService.dbGetOneRow
   */
  async dbGetOneRow(
    sql: string,
    params?: unknown[],
  ): Promise<unknown[] | undefined> {
    await this.waitForInitialization();
    const result = await this.queueOperation<QueryExecResult>(
      "query",
      sql,
      params || [],
    );

    // Return the first row from the result, or undefined if no results
    if (result && result.values && result.values.length > 0) {
      return result.values[0];
    }
    return undefined;
  }

  /**
   * @see PlatformService.dbRawQuery
   */
  async dbRawQuery(sql: string, params?: unknown[]): Promise<unknown> {
    await this.waitForInitialization();
    return this.queueOperation("rawQuery", sql, params || []);
  }

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

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

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

  // --- PWA/Web-only methods (no-op for Capacitor) ---
  public registerServiceWorker(): void {}

  // Database utility methods - inherited from BaseDatabaseService
  // generateInsertStatement, updateDefaultSettings, updateActiveDid,
  // getActiveIdentity, insertNewDidIntoSettings, updateDidSpecificSettings,
  // retrieveSettingsForActiveAccount are all inherited from BaseDatabaseService
}
