/**
 * Test User Zero Configuration
 * 
 * Based on TimeSafari crowd-master User Zero configuration
 * This provides the necessary credentials and settings for testing
 * the daily notification plugin with stars querying functionality.
 * 
 * @author Matthew Raymer
 * @version 1.0.0
 */

// Lazy import logger to avoid ES module issues when loaded by Capacitor CLI (CommonJS)
// Logger is only used inside functions, not at module scope
let logger: { error: (...args: unknown[]) => void; custom: (emoji: string, ...args: unknown[]) => void; info: (...args: unknown[]) => void } | null = null;
const getLogger = async (): Promise<{ error: (...args: unknown[]) => void; custom: (emoji: string, ...args: unknown[]) => void; info: (...args: unknown[]) => void } | null> => {
  if (!logger) {
    const loggerModule = await import('../lib/logger');
    logger = loggerModule.logger;
  }
  return logger;
};

export const TEST_USER_ZERO_CONFIG = {
  // User Zero Identity (from crowd-master testUtils.ts)
  identity: {
    did: "did:ethr:0x0000694B58C2cC69658993A90D3840C560f2F51F",
    name: "User Zero",
    seedPhrase: "rigid shrug mobile smart veteran half all pond toilet brave review universe ship congress found yard skate elite apology jar uniform subway slender luggage",
    // User Zero's known public key for verification
    // Public Key (hex): 03b231e73413f1d36b3327763ad9a44bb81fc91480a14977ab3cfd32df0564af1d
    // Public Key (base64): A7Ix5zQT8dNrMyd2OtmkS7gfyRSAoUl3qzz9Mt8FZK8d
    // Derivation Path: m/84737769'/0'/0'/0'
    expectedPublicKeyHex: "03b231e73413f1d36b3327763ad9a44bb81fc91480a14977ab3cfd32df0564af1d",
    expectedPublicKeyBase64: "A7Ix5zQT8dNrMyd2OtmkS7gfyRSAoUl3qzz9Mt8FZK8d",
    derivationPath: "m/84737769'/0'/0'/0'"
  },

  // API Configuration
  api: {
    // API server selection:
    // - "localhost": Use 10.0.2.2:PORT (Android emulator special alias for host localhost)
    // - "staging": Use staging API server
    // - "production": Use production API server
    // - "mock": Use mock responses (no network calls)
    // - "custom": Use servers.custom URL
    serverMode: "localhost" as "localhost" | "staging" | "production" | "mock" | "custom",
    
    // Server URLs for different modes
    servers: {
      // Android emulator: use 10.0.2.2 to access host machine's localhost
      // iOS simulator: use localhost or 127.0.0.1
      localhost: {
        android: "http://10.0.2.2:3000", // Default local dev server port
        ios: "http://localhost:3000",
        web: "http://localhost:3000"
      },
      staging: "https://api-staging.timesafari.com",
      production: "https://api.timesafari.com",
      custom: "" // Set this if using custom URL
    },
    
    // Stars querying endpoint (from crowd-master endorserServer.ts)
    starsEndpoint: "/api/v2/report/plansLastUpdatedBetween",
    
    // Authentication
    jwtExpirationMinutes: 1, // Short-lived tokens like crowd-master
    jwtAlgorithm: "HS256"
  },
  
  /**
   * Get current API server URL based on mode and platform
   * Call this function instead of accessing api.server directly
   */
  getApiServerUrl(): string {
    if (TEST_USER_ZERO_CONFIG.api.serverMode === "mock") {
      return "mock://localhost"; // Not used, but needed for type checking
    }
    if (TEST_USER_ZERO_CONFIG.api.serverMode === "localhost") {
      // Detect platform and use appropriate localhost address
      if (typeof window !== "undefined") {
        const isAndroid = /Android/i.test(navigator.userAgent);
        const isIOS = /iPhone|iPad|iPod/i.test(navigator.userAgent);
        
        if (isAndroid) {
          return TEST_USER_ZERO_CONFIG.api.servers.localhost.android;
        } else if (isIOS) {
          return TEST_USER_ZERO_CONFIG.api.servers.localhost.ios;
        } else {
          return TEST_USER_ZERO_CONFIG.api.servers.localhost.web;
        }
      }
      return TEST_USER_ZERO_CONFIG.api.servers.localhost.android; // Default to Android for Capacitor
    }
    if (TEST_USER_ZERO_CONFIG.api.serverMode === "custom" && TEST_USER_ZERO_CONFIG.api.servers.custom) {
      return TEST_USER_ZERO_CONFIG.api.servers.custom;
    }
    return TEST_USER_ZERO_CONFIG.api.servers[TEST_USER_ZERO_CONFIG.api.serverMode] as string;
  },

  // Test Starred Projects (mock data for testing)
  starredProjects: {
    // IMPORTANT: Plan handle IDs must be valid URIs (RFC 3986 format)
    // 
    // Default format: https://endorser.ch/entity/{26-char-ULID}
    // - ULID is 26 characters, Crockford base32 (e.g., 01GQBE7Q0RQQAGJMEEW6RSGKTF)
    // - Any valid URI scheme is accepted: http://, https://, did:, etc.
    //
    // To get valid plan IDs:
    // 1. Create projects in TimeSafari app and note handleId from API responses
    // 2. Query your local database: SELECT handleId FROM plans LIMIT 5
    // 3. Check starred projects: settings.starredPlanHandleIds
    // 4. See docs/getting-valid-plan-ids.md for detailed instructions
    //
    // These are auto-generated valid URIs for testing structure.
    // Replace with real IDs from your TimeSafari database for actual testing.
    planIds: ((): string[] => {
      // Generate valid URI format IDs for testing
      const generateULID = (): string => {
        const alphabet = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
        const timestamp = Date.now().toString(36).toUpperCase().padStart(10, '0');
        const random = Array.from({ length: 16 }, () => 
          alphabet[Math.floor(Math.random() * alphabet.length)]
        ).join('');
        return (timestamp + random).substring(0, 26);
      };
      
      return [
        `https://endorser.ch/entity/${generateULID()}`,
        `https://endorser.ch/entity/${generateULID()}`,
        `https://endorser.ch/entity/${generateULID()}`
      ];
    })(),
    
    // Last acknowledged JWT ID (for pagination testing)
    lastAckedJwtId: "1704067200_abc123_def45678"
  },

  // Notification Configuration
  notifications: {
    // Fetch timing: 5 minutes before notification (as requested)
    fetchLeadTimeMinutes: 5,
    
    // Schedule time for testing
    scheduleTime: "09:00",
    
    // Test notification content
    defaultTitle: "Daily Stars Update",
    defaultBody: "New changes detected in your starred projects!"
  },

  // Testing Configuration
  testing: {
    // Enable mock responses for offline testing
    // When true, API calls return mock data instead of making network requests
    enableMockResponses: true,
    
    // Localhost testing configuration
    localhost: {
      // Port your local dev server runs on
      port: 3000,
      
      // Enable HTTPS for localhost (requires self-signed cert in emulator)
      useHttps: false,
      
      // Additional headers for localhost requests
      headers: {
        "X-Development-Mode": "true"
      }
    },
    
    // Network timeouts
    timeoutMs: 30000,
    retryAttempts: 3,
    retryDelayMs: 1000,
    
    // Debug settings
    debugMode: true,
    logLevel: "INFO"
  }
} as const;

/**
 * Mock starred projects response for offline testing
 * Based on crowd-master test fixtures
 */
export const MOCK_STARRED_PROJECTS_RESPONSE = {
  data: [
    {
      planSummary: {
        jwtId: "1704067200_abc123_def45678",
        handleId: "test_project_1",
        name: "Test Project 1",
        description: "First test project for User Zero",
        issuerDid: "did:key:test_issuer_1",
        agentDid: "did:key:test_agent_1",
        startTime: "2025-01-01T00:00:00Z",
        endTime: "2025-01-31T23:59:59Z",
        locLat: 40.7128,
        locLon: -74.0060,
        url: "https://test-project-1.com"
      },
      previousClaim: {
        jwtId: "1703980800_xyz789_0badf00d",
        claimType: "project_update"
      }
    },
    {
      planSummary: {
        jwtId: "1704153600_mno345_0badf00d",
        handleId: "test_project_2",
        name: "Test Project 2",
        description: "Second test project for User Zero",
        issuerDid: "did:key:test_issuer_2",
        agentDid: "did:key:test_agent_2",
        startTime: "2025-02-01T00:00:00Z",
        endTime: "2025-02-28T23:59:59Z",
        locLat: null,
        locLon: null
      },
      previousClaim: {
        jwtId: "1704067200_stu901_1cafebad",
        claimType: "project_update"
      }
    }
  ],
  hitLimit: false,
  pagination: {
    hasMore: false,
    nextAfterId: null
  }
} as const;

/**
 * Generate ES256K signed JWT token for User Zero using DID-based signing
 * 
 * This function generates a JWT token signed with User Zero's private key,
 * using User Zero's DID and seed phrase from TEST_USER_ZERO_CONFIG.
 * 
 * Uses TimeSafari's approach (mimics createEndorserJwtForKey() function):
 * - did-jwt library with ES256K algorithm (DID-based signing)
 * - Ethereum private key derived from User Zero's seed phrase
 * - JWT payload includes User Zero's DID as issuer and subject
 * 
 * @returns Promise<string> ES256K signed JWT token for User Zero
 */
export async function generateEndorserJWT(): Promise<string> {
  try {
    // Dynamic import to avoid loading did-jwt in environments where it's not needed
    const { createJWT, SimpleSigner } = await import('did-jwt');
    const { HDNodeWallet, Mnemonic } = await import('ethers');
    
    // Derive Ethereum private key from User Zero's seed phrase
    // Using User Zero's specific derivation path: m/84737769'/0'/0'/0'
    // This is the path used when User Zero was registered on the server
    // NOTE: Must use fromMnemonic() with the Mnemonic object, not fromPhrase() with phrase string
    const userZeroSeedPhrase = TEST_USER_ZERO_CONFIG.identity.seedPhrase;
    const mnemonic = Mnemonic.fromPhrase(userZeroSeedPhrase);
    const derivationPath = "m/84737769'/0'/0'/0'";
    const wallet = HDNodeWallet.fromMnemonic(mnemonic, derivationPath);
    const privateKeyHex = wallet.privateKey.slice(2); // Remove '0x' prefix
    
    // Extract public key from wallet in compressed format
    // ethers.js wallet.signingKey.compressedPublicKey provides the compressed format
    // Compressed format: 33 bytes (0x02 or 0x03 prefix + 32-byte x-coordinate)
    const signingKey = wallet.signingKey;
    const compressedPublicKey = signingKey.compressedPublicKey; // This should be the compressed public key
    const publicKeyHexRaw = compressedPublicKey ? compressedPublicKey.slice(2).toLowerCase() : null;
    
    // Fallback: if compressed public key not available, use first 66 hex chars from uncompressed
    // Note: This is a workaround - we should have compressedPublicKey from signingKey
    const publicKeyHexFinal = publicKeyHexRaw || wallet.publicKey.slice(2).substring(0, 66).toLowerCase();
    
    // Verify derived address matches User Zero's DID
    const derivedAddress = wallet.address.toLowerCase();
    const expectedAddress = TEST_USER_ZERO_CONFIG.identity.did.replace('did:ethr:', '').toLowerCase();
    const logger = await getLogger();
    
    // Verify public key matches expected (if provided)
    const expectedPublicKeyHex = TEST_USER_ZERO_CONFIG.identity.expectedPublicKeyHex;
    
    // Comprehensive verification logging
    if (derivedAddress !== expectedAddress) {
      logger.error('⚠️ Key derivation address mismatch!', {
        derivedAddress: derivedAddress,
        expectedAddress: expectedAddress,
        derivationPath: derivationPath,
        note: 'JWT signing may fail if server expects different key'
      });
      logger.error('⚠️ CRITICAL: Derived address does not match User Zero DID!', {
        derived: derivedAddress,
        expected: expectedAddress,
        path: derivationPath,
        warning: 'JWT verification will likely fail on server'
      });
    } else {
      logger.custom('✅', 'Key derivation verified - address matches User Zero DID');
    }
    
    // Verify public key if expected value is provided
    if (expectedPublicKeyHex && publicKeyHexFinal) {
      // Compare the derived public key with expected (both should be compressed format)
      const publicKeyMatchesActual = publicKeyHexFinal.toLowerCase() === expectedPublicKeyHex.toLowerCase();
      
      if (!publicKeyMatchesActual) {
        logger.error('⚠️ CRITICAL: Derived public key does not match expected!', {
          derivedPublicKey: publicKeyHexFinal,
          expectedPublicKey: expectedPublicKeyHex,
          derivationPath: derivationPath,
          warning: 'Server will not recognize this public key - JWT verification will fail'
        });
        logger.error('⚠️ Public Key Mismatch Details:', {
          derived: publicKeyHexFinal.substring(0, 10) + '...' + publicKeyHexFinal.substring(publicKeyHexFinal.length - 10),
          expected: expectedPublicKeyHex.substring(0, 10) + '...' + expectedPublicKeyHex.substring(expectedPublicKeyHex.length - 10),
          derivedLength: publicKeyHexFinal.length,
          expectedLength: expectedPublicKeyHex.length,
          derivedPrefix: publicKeyHexFinal.substring(0, 2), // Should be 02 or 03
          expectedPrefix: expectedPublicKeyHex.substring(0, 2),
          note: 'Verify derivation path and seed phrase are correct'
        });
      } else {
        logger.custom('✅', 'Public key verified - matches expected User Zero public key');
        logger.info('✅ Public Key Verification:', {
          derived: publicKeyHexFinal.substring(0, 20) + '...' + publicKeyHexFinal.substring(publicKeyHexFinal.length - 20),
          expected: expectedPublicKeyHex.substring(0, 20) + '...' + expectedPublicKeyHex.substring(expectedPublicKeyHex.length - 20),
          match: true
        });
      }
    }
    
    // Log comprehensive key derivation diagnostics
    logger.info('🔐 JWT Key Derivation: Complete diagnostics', {
      address: derivedAddress,
      addressMatch: derivedAddress === expectedAddress,
      derivationPath: derivationPath,
      publicKeyHex: publicKeyHexFinal ? (publicKeyHexFinal.substring(0, 20) + '...' + publicKeyHexFinal.substring(publicKeyHexFinal.length - 20)) : 'N/A',
      expectedPublicKeyHex: expectedPublicKeyHex ? (expectedPublicKeyHex.substring(0, 20) + '...' + expectedPublicKeyHex.substring(expectedPublicKeyHex.length - 20)) : 'N/A',
      publicKeyMatch: expectedPublicKeyHex && publicKeyHexFinal ? (publicKeyHexFinal.toLowerCase() === expectedPublicKeyHex.toLowerCase()) : null,
      publicKeyLength: publicKeyHexFinal?.length || 0,
      expectedPublicKeyLength: expectedPublicKeyHex?.length || 0,
      hasCompressedPublicKey: !!compressedPublicKey
    });
    
    // Create signer for ES256K (Ethereum secp256k1 curve) using User Zero's private key
    const signer = SimpleSigner(privateKeyHex);
    
    // Create JWT payload with User Zero's DID as issuer and subject
    const nowEpoch = Math.floor(Date.now() / 1000);
    const expiresIn = TEST_USER_ZERO_CONFIG.api.jwtExpirationMinutes * 60;
    const userZeroDid = TEST_USER_ZERO_CONFIG.identity.did;
    
    const payload = {
      // Standard JWT claims - all using User Zero's DID
      iat: nowEpoch,
      exp: nowEpoch + expiresIn,
      iss: userZeroDid, // User Zero's DID as issuer
      sub: userZeroDid, // User Zero's DID as subject
      // NOTE: aud (audience) claim is omitted because server's did-jwt verifyJWT()
      // requires audience option when aud is present, but server isn't configured
      // to validate it. Server will reject JWTs with aud claim until it adds support.
    };
    
    // Create ES256K signed JWT using User Zero's DID and private key
    // ES256K is the default algorithm for did-jwt
    // NOTE: sub claim is included in payload; aud is omitted because server's
    // did-jwt verifyJWT() requires audience option when aud is present, but
    // server isn't configured to validate it
    const jwt = await createJWT(payload, {
      issuer: userZeroDid, // User Zero's DID (overwrites payload.iss)
      signer: signer, // User Zero's private key signer
      expiresIn: expiresIn
    });
    
    logger.custom("🔐", "JWT generated for User Zero - Algorithm: ES256K, DID:", userZeroDid.substring(0, 30) + "...");
    logger.custom("🔐", "JWT length:", jwt.length, "characters");
    
    // Enhanced diagnostic logging
    logger.info('🔐 JWT Generation Diagnostics:', {
      algorithm: 'ES256K',
      issuerDid: userZeroDid,
      issuerAddress: userZeroDid.replace('did:ethr:', '').toLowerCase(),
      derivedAddress: derivedAddress,
      derivationPath: derivationPath,
      addressMatch: derivedAddress === expectedAddress,
      jwtLength: jwt.length,
      jwtPreview: jwt.substring(0, 20) + '...' + jwt.substring(jwt.length - 20),
      expiresIn: expiresIn,
      nowEpoch: nowEpoch
    });
    
    return jwt;
  } catch (error) {
    const logger = await getLogger();
    logger.error('Failed to generate ES256K JWT:', error);
    throw new Error(`JWT generation failed: ${error instanceof Error ? error.message : String(error)}`);
  }
}

/**
 * Generate test JWT token for User Zero (DEPRECATED - use generateEndorserJWT instead)
 * 
 * @deprecated This function generates an unsigned test token. Use generateEndorserJWT()
 * for real ES256K signed tokens that work with the endorser-ch API.
 */
export function generateTestJWT(): string {
  const nowEpoch = Math.floor(Date.now() / 1000);
  const endEpoch = nowEpoch + TEST_USER_ZERO_CONFIG.api.jwtExpirationMinutes * 60;
  
  const header = {
    alg: TEST_USER_ZERO_CONFIG.api.jwtAlgorithm,
    typ: "JWT"
  };
  
  const payload = {
    exp: endEpoch,
    iat: nowEpoch,
    iss: TEST_USER_ZERO_CONFIG.identity.did,
    sub: TEST_USER_ZERO_CONFIG.identity.did
  };
  
  // Simple base64 encoding for testing (not cryptographically secure)
  const encodedHeader = btoa(JSON.stringify(header));
  const encodedPayload = btoa(JSON.stringify(payload));
  const signature = "test_signature_for_development_only";
  
  return `${encodedHeader}.${encodedPayload}.${signature}`;
}

/**
 * Test User Zero API client for stars querying
 */
export class TestUserZeroAPI {
  private baseUrl: string;
  private jwt: string;

  constructor(baseUrl?: string) {
    // Use provided URL, or get from config based on serverMode
    this.baseUrl = baseUrl || (TEST_USER_ZERO_CONFIG.api.serverMode === "mock" 
      ? "mock://localhost" 
      : TEST_USER_ZERO_CONFIG.getApiServerUrl());
    this.jwt = generateTestJWT();
  }

  /**
   * Set API server URL (useful for switching between localhost, staging, etc.)
   */
  async setBaseUrl(url: string): Promise<void> {
    this.baseUrl = url;
    const log = await getLogger();
    log.custom("🔧", "API base URL updated to:", url);
  }

  /**
   * Query starred projects for changes
   * Mimics crowd-master getStarredProjectsWithChanges function
   */
  async getStarredProjectsWithChanges(
    starredPlanIds: string[],
    afterId?: string
  ): Promise<typeof MOCK_STARRED_PROJECTS_RESPONSE> {
    // Check if we should use mock responses
    const useMock = TEST_USER_ZERO_CONFIG.testing.enableMockResponses ||
                    TEST_USER_ZERO_CONFIG.api.serverMode === "mock";
    
    if (useMock) {
      // Return mock data for offline testing
      const log = await getLogger();
      log.custom("🧪", "Using mock starred projects response");
      return MOCK_STARRED_PROJECTS_RESPONSE;
    }

    // Real API call (when mock is disabled)
    const baseUrl = this.baseUrl || TEST_USER_ZERO_CONFIG.getApiServerUrl();
    const url = `${baseUrl}${TEST_USER_ZERO_CONFIG.api.starsEndpoint}`;
    const headers = {
      'Authorization': `Bearer ${this.jwt}`,
      'Content-Type': 'application/json',
      'User-Agent': 'TimeSafari-DailyNotificationPlugin/1.0.0'
    };

    const requestBody = {
      planIds: starredPlanIds,
      afterId: afterId || TEST_USER_ZERO_CONFIG.starredProjects.lastAckedJwtId
    };

    const log = await getLogger();
    log.custom("🌐", "Making real API call to:", url);
    log.custom("📦", "Request body:", requestBody);

    const response = await fetch(url, {
      method: 'POST',
      headers,
      body: JSON.stringify(requestBody)
    });

    if (!response.ok) {
      throw new Error(`API call failed: ${response.status} ${response.statusText}`);
    }

    return await response.json();
  }

  /**
   * Refresh JWT token
   */
  async refreshToken(): Promise<void> {
    this.jwt = generateTestJWT();
    const log = await getLogger();
    log.custom("🔄", "JWT token refreshed");
  }

  /**
   * Get current JWT token
   */
  getJWT(): string {
    return this.jwt;
  }
}

export default TEST_USER_ZERO_CONFIG;
