/**
 * P2P Withdrawal Processing Edge Function
 *
 * This function processes pending withdrawal requests from the P2P internal balance system.
 * It should be called by a cron job or triggered manually by admin.
 *
 * Security:
 * - Uses service role key for database operations
 * - Platform wallet private key stored in environment variables
 * - All transactions are logged for auditing
 *
 * Flow:
 * 1. Fetch pending withdrawal requests
 * 2. For each request:
 *    a. Lock the request (status = 'processing')
 *    b. Execute blockchain transfer
 *    c. Update request with tx hash
 *    d. Deduct from user's internal balance
 *    e. Mark as completed
 */

import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'
import { createClient } from 'npm:@supabase/supabase-js@2'
import { ApiPromise, WsProvider, Keyring } from 'npm:@pezkuwi/api@16.5.36'
import { cryptoWaitReady } from 'npm:@pezkuwi/util-crypto@14.0.25'

// Configuration
const SUPABASE_URL = Deno.env.get("SUPABASE_URL")!;
const SUPABASE_SERVICE_ROLE_KEY = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!;
const PLATFORM_WALLET_MNEMONIC = Deno.env.get("PLATFORM_WALLET_MNEMONIC")!;
const RPC_ENDPOINT = Deno.env.get("RPC_ENDPOINT") || "wss://asset-hub-rpc.pezkuwichain.io";

// Asset IDs
const ASSET_IDS: Record<string, number | null> = {
  HEZ: null, // Native token
  PEZ: 1,
};

// Decimals
const DECIMALS = 12;

interface WithdrawRequest {
  id: string;
  user_id: string;
  token: string;
  amount: number;
  wallet_address: string;
  status: string;
}

/**
 * Process a single withdrawal request
 */
async function processWithdrawal(
  api: ApiPromise,
  keyring: Keyring,
  supabase: ReturnType<typeof createClient>,
  request: WithdrawRequest
): Promise<{ success: boolean; txHash?: string; error?: string }> {
  const { id, token, amount, wallet_address } = request;

  try {
    console.log(`Processing withdrawal ${id}: ${amount} ${token} to ${wallet_address}`);

    // 1. Get platform wallet from keyring
    const platformWallet = keyring.addFromUri(PLATFORM_WALLET_MNEMONIC);

    // 2. Calculate amount in planck (smallest unit)
    const amountPlanck = BigInt(Math.floor(amount * Math.pow(10, DECIMALS)));

    console.log(`Sending ${amount} ${token}: ${platformWallet.address} → ${wallet_address}`);

    let tx;
    if (token === "HEZ" || ASSET_IDS[token] === null) {
      tx = api.tx.balances.transferKeepAlive(wallet_address, amountPlanck);
    } else {
      const assetId = ASSET_IDS[token];
      if (assetId === undefined) {
        throw new Error(`Unknown token: ${token}`);
      }
      tx = api.tx.assets.transfer(assetId, wallet_address, amountPlanck);
    }

    // 3. Fetch nonce
    const accountInfo = await api.query.system.account(platformWallet.address);
    const nonce = accountInfo.nonce;

    // 5. Sign and send transaction
    const txHash = await new Promise<string>((resolve, reject) => {
      let unsubscribe: () => void;

      tx.signAndSend(platformWallet, { nonce }, ({ status, dispatchError }) => {
        if (dispatchError) {
          if (dispatchError.isModule) {
            const decoded = api.registry.findMetaError(dispatchError.asModule);
            reject(new Error(`${decoded.section}.${decoded.name}: ${decoded.docs.join(" ")}`));
          } else {
            reject(new Error(dispatchError.toString()));
          }
          if (unsubscribe) unsubscribe();
          return;
        }

        if (status.isInBlock) {
          console.log(`Transaction in block: ${status.asInBlock.toString()}`);
        }

        if (status.isFinalized) {
          const hash = tx.hash.toHex();
          console.log(`Transaction finalized: ${hash}`);
          resolve(hash);
          if (unsubscribe) unsubscribe();
        }
      })
        .then((unsub) => {
          unsubscribe = unsub;
        })
        .catch(reject);
    });

    // 5. Update request with tx hash and complete status
    const { error: updateError } = await supabase
      .from("p2p_deposit_withdraw_requests")
      .update({
        status: "completed",
        blockchain_tx_hash: txHash,
        processed_at: new Date().toISOString(),
      })
      .eq("id", id);

    if (updateError) {
      console.error(`Failed to update request ${id}:`, updateError);
      // Transaction was successful, but we couldn't update the database
      // This should be handled manually
      return { success: true, txHash, error: "Database update failed after successful tx" };
    }

    // 6. Log the withdrawal in balance transactions
    const { error: logError } = await supabase.from("p2p_balance_transactions").insert({
      user_id: request.user_id,
      token,
      transaction_type: "withdraw",
      amount: -amount,
      balance_before: 0, // Will be calculated by trigger
      balance_after: 0, // Will be calculated by trigger
      reference_type: "withdrawal",
      reference_id: id,
      description: `Withdrawal to ${wallet_address.slice(0, 8)}...${wallet_address.slice(-6)}`,
    });

    if (logError) {
      console.error(`Failed to log transaction for ${id}:`, logError);
    }

    console.log(`Successfully processed withdrawal ${id}: ${txHash}`);
    return { success: true, txHash };
  } catch (error: unknown) {
    const errorMessage = error instanceof Error ? error.message : "Unknown error";
    console.error(`Failed to process withdrawal ${id}:`, errorMessage);

    // Update request with error
    await supabase
      .from("p2p_deposit_withdraw_requests")
      .update({
        status: "failed",
        error_message: errorMessage,
        processed_at: new Date().toISOString(),
      })
      .eq("id", id);

    return { success: false, error: errorMessage };
  }
}

/**
 * Main handler
 */
serve(async (req: Request) => {
  // Allowed origins for CORS
  const ALLOWED_ORIGINS = [
    'https://app.pezkuwichain.io',
    'https://www.pezkuwichain.io',
    'https://pezkuwichain.io',
  ]

  const requestOrigin = req.headers.get('Origin')
  const allowedOrigin = requestOrigin && ALLOWED_ORIGINS.includes(requestOrigin) ? requestOrigin : ALLOWED_ORIGINS[0]

  const headers = {
    "Access-Control-Allow-Origin": allowedOrigin,
    "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
    "Access-Control-Allow-Credentials": "true",
    "Content-Type": "application/json",
  };

  // Handle CORS preflight
  if (req.method === "OPTIONS") {
    return new Response("ok", { headers });
  }

  try {
    // Authorization: this batch processor drains ALL pending withdrawal requests
    // to their destination wallets, so it MUST be restricted to the backend
    // service role (cron / admin tooling). Merely having *any* bearer token
    // (e.g. the public anon key) is NOT sufficient — previously that let anyone
    // trigger mass processing.
    const authHeader = req.headers.get("Authorization");
    const bearer = authHeader?.replace(/^Bearer\s+/i, "").trim();
    if (!bearer || bearer !== SUPABASE_SERVICE_ROLE_KEY) {
      return new Response(
        JSON.stringify({ error: "Unauthorized: service role required" }),
        { status: 401, headers }
      );
    }

    // Initialize Supabase client with service role
    const supabase = createClient(SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY);

    // Parse request body for optional filters
    let requestBody: { limit?: number; requestId?: string } = {};
    try {
      requestBody = await req.json();
    } catch {
      // No body provided, use defaults
    }

    const { limit = 10, requestId } = requestBody;

    // 1. Fetch pending withdrawal requests
    let query = supabase
      .from("p2p_deposit_withdraw_requests")
      .select("*")
      .eq("request_type", "withdraw")
      .eq("status", "pending")
      .order("created_at", { ascending: true })
      .limit(limit);

    // If specific request ID provided, fetch only that one
    if (requestId) {
      query = supabase
        .from("p2p_deposit_withdraw_requests")
        .select("*")
        .eq("id", requestId)
        .eq("request_type", "withdraw")
        .in("status", ["pending", "failed"]);
    }

    const { data: pendingRequests, error: fetchError } = await query;

    if (fetchError) {
      throw new Error(`Failed to fetch pending requests: ${fetchError.message}`);
    }

    if (!pendingRequests || pendingRequests.length === 0) {
      return new Response(
        JSON.stringify({ message: "No pending withdrawal requests", processed: 0 }),
        { headers }
      );
    }

    console.log(`Found ${pendingRequests.length} pending withdrawal requests`);

    // 2. Lock the requests (set status to processing)
    const requestIds = pendingRequests.map((r: WithdrawRequest) => r.id);
    const { error: lockError } = await supabase
      .from("p2p_deposit_withdraw_requests")
      .update({ status: "processing" })
      .in("id", requestIds);

    if (lockError) {
      throw new Error(`Failed to lock requests: ${lockError.message}`);
    }

    // 3. Initialize Polkadot API
    console.log(`Connecting to blockchain: ${RPC_ENDPOINT}`);
    await cryptoWaitReady();

    const provider = new WsProvider(RPC_ENDPOINT);
    const api = await ApiPromise.create({ provider });
    await api.isReady;

    const keyring = new Keyring({ type: "sr25519" });

    // 4. Process each withdrawal
    const results = [];
    for (const request of pendingRequests as WithdrawRequest[]) {
      const result = await processWithdrawal(api, keyring, supabase, request);
      results.push({
        id: request.id,
        ...result,
      });

      // Small delay between transactions to avoid nonce issues
      await new Promise((resolve) => setTimeout(resolve, 1000));
    }

    // 5. Cleanup
    await api.disconnect();

    // 6. Return results
    const successCount = results.filter((r) => r.success).length;
    const failCount = results.filter((r) => !r.success).length;

    return new Response(
      JSON.stringify({
        message: `Processed ${results.length} withdrawal requests`,
        processed: results.length,
        success: successCount,
        failed: failCount,
        results,
      }),
      { headers }
    );
  } catch (error: unknown) {
    const errorMessage = error instanceof Error ? error.message : "Unknown error";
    console.error("Error processing withdrawals:", errorMessage);

    return new Response(
      JSON.stringify({ error: errorMessage }),
      { status: 500, headers }
    );
  }
});
