import fs from 'fs';
import path from 'path';
import { GatewaySecretConfig, ServerTransaction, PaymentStatus } from './types';

// Default initial gateway configurations (all sandbox by default)
const DEFAULT_GATEWAYS: GatewaySecretConfig[] = [
  {
    id: 'bkash',
    name: 'bKash Direct (Tokenized API)',
    is_enabled: true,
    mode: 'test',
    username: 'sandboxTestUser',
    password: 'sandboxTestPassword',
    app_key: 'bkash_sandbox_app_key_2026',
    app_secret: 'bkash_sandbox_app_secret_998124',
    merchant_mobile: '01770000000',
    api_url: 'https://tokenized.sandbox.bka.sh/v1.2.0-beta/tokenized/checkout',
    callback_url: '/api/payments/bkash/callback',
  },
  {
    id: 'nagad',
    name: 'Nagad PGW (E-Commerce)',
    is_enabled: true,
    mode: 'test',
    merchant_id: '683002007104225',
    merchant_mobile: '01800000000',
    public_key: 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0nagad_test_public_key',
    private_key: 'MIIEowIBAAKCAQEA0nagad_test_private_key_secure_server_only',
    api_url: 'http://sandbox.mynagad.com:10080/remote-payment-gateway-1.0/api/dfs',
    callback_url: '/api/payments/nagad/callback',
  },
  {
    id: 'rocket',
    name: 'DBBL Rocket Pay (MFS Gateway)',
    is_enabled: true,
    mode: 'test',
    merchant_id: '019000000009',
    app_key: 'rocket_terminal_key_test_2026',
    terminal_id: 'TERM_DBBL_TEST_01',
    pin_password: 'rocket_secret_pin_test',
    api_url: 'https://rocket-sandbox.dutchbanglabank.com/api',
    callback_url: '/api/payments/rocket/callback',
  },
  {
    id: 'sslcommerz',
    name: 'SSLCommerz (Cards, MFS, Internet Banking)',
    is_enabled: true,
    mode: 'test',
    store_id: 'topbd_sandbox_01',
    store_password: 'topbd_sandbox_password_99',
    api_url: 'https://sandbox.sslcommerz.com',
    success_url: '/api/payments/sslcommerz/callback',
    fail_url: '/api/payments/sslcommerz/callback',
    cancel_url: '/api/payments/sslcommerz/callback',
    ipn_url: '/api/payments/sslcommerz/ipn',
    allowed_channels: ['cards', 'bkash', 'nagad', 'rocket', 'internet_banking'],
  },
  {
    id: 'shurjopay',
    name: 'shurjoPay Merchant Gateway',
    is_enabled: true,
    mode: 'test',
    merchant_id: 'sp_test_merchant',
    username: 'sp_user',
    password: 'sp_password',
    api_url: 'https://sandbox.shurjopayment.com',
    callback_url: '/api/payments/shurjopay/callback',
  },
  {
    id: 'aamarpay',
    name: 'aamarPay Merchant Gateway',
    is_enabled: true,
    mode: 'test',
    store_id: 'aamarpaytest',
    signature_key: 'dbb74892e8241a45a7f0144916d6664e',
    api_url: 'https://sandbox.aamarpay.com',
    success_url: '/api/payments/aamarpay/callback',
    fail_url: '/api/payments/aamarpay/callback',
    cancel_url: '/api/payments/aamarpay/callback',
    ipn_url: '/api/payments/aamarpay/ipn',
    callback_url: '/api/payments/aamarpay/callback',
    session_timeout: 15,
    allowed_channels: ['cards', 'bkash', 'nagad', 'rocket', 'internet_banking'],
  },
  {
    id: 'uddoktapay',
    name: 'UddoktaPay Automated PGW',
    is_enabled: true,
    mode: 'test',
    api_key: process.env.UDDOKTAPAY_API_KEY || '',
    api_url: 'https://sandbox.uddoktapay.com',
    callback_url: '/api/payments/uddoktapay/callback',
    success_url: '/api/payments/uddoktapay/callback',
    cancel_url: '/api/payments/uddoktapay/callback?status=cancelled',
    ipn_url: '/api/payments/uddoktapay/ipn',
    webhook_url: '/api/payments/uddoktapay/ipn',
    allowed_channels: ['bkash', 'nagad', 'rocket', 'upay', 'cards'],
  },
];

class PaymentStore {
  private transactions: Map<string, ServerTransaction> = new Map();
  private gatewayConfigs: Map<string, GatewaySecretConfig> = new Map();
  private defaultGateway: string = 'bkash';
  private gatewaysFilePath: string = path.join(process.cwd(), 'data', 'gateways_config.json');
  private paymentsDbPath: string = path.join(process.cwd(), 'data', 'payments_db.json');

  constructor() {
    // 1. Initialize default gateway configurations
    DEFAULT_GATEWAYS.forEach(gw => {
      this.gatewayConfigs.set(gw.id, { ...gw });
    });

    // 2. Load persistent gateway configurations from disk
    this.loadGatewaysFromDisk();

    // 3. Load persistent transaction database from disk (or seed if new)
    this.loadTransactionsFromDisk();
  }

  // --- Transactions Persistent Database Storage ---

  private loadTransactionsFromDisk(): void {
    try {
      if (fs.existsSync(this.paymentsDbPath)) {
        const raw = fs.readFileSync(this.paymentsDbPath, 'utf-8');
        const data = JSON.parse(raw);
        if (Array.isArray(data.transactions)) {
          this.transactions.clear();
          data.transactions.forEach((txn: ServerTransaction) => {
            if (txn && txn.transaction_id) {
              this.transactions.set(txn.transaction_id, txn);
            }
          });
          return;
        }
      }
    } catch (e) {
      console.warn('Could not read payments_db.json, re-initializing from seeds:', e);
    }

    // Seed default demo transactions if file does not exist or was invalid
    this.seedDemoTransactions();
    this.saveTransactionsToDisk();
  }

  private saveTransactionsToDisk(): void {
    try {
      const dir = path.dirname(this.paymentsDbPath);
      if (!fs.existsSync(dir)) {
        fs.mkdirSync(dir, { recursive: true });
      }
      const data = {
        version: 1,
        total: this.transactions.size,
        updatedAt: new Date().toISOString(),
        transactions: Array.from(this.transactions.values()),
      };
      fs.writeFileSync(this.paymentsDbPath, JSON.stringify(data, null, 2), 'utf-8');
    } catch (e) {
      console.error('CRITICAL: Failed to persist payments_db.json to disk:', e);
    }
  }

  private seedDemoTransactions() {
    const demo: ServerTransaction[] = [
      {
        id: 'pay_1',
        transaction_id: 'TXN-BKASH-9821387',
        listing_id: 'list_1',
        listing_name: 'Chaldal Online Grocery',
        listing_url: 'https://chaldal.com',
        listing_image: 'https://images.unsplash.com/photo-1542838132-92c53300491e?w=200&auto=format&fit=crop&q=80',
        category_id: 'cat_ecom',
        amount: 25000,
        currency: 'BDT',
        gateway: 'bkash',
        mode: 'live',
        status: 'paid',
        created_at: '2026-08-24T18:45:00Z',
        updated_at: '2026-08-24T18:45:04Z',
        paid_at: '2026-08-24T18:45:04Z',
        callback_processed_at: '2026-08-24T18:45:04Z',
      },
      {
        id: 'pay_2',
        transaction_id: 'TXN-NAGAD-4412098',
        listing_id: 'list_2',
        listing_name: 'Pathao Super App',
        listing_url: 'https://pathao.com',
        listing_image: 'https://images.unsplash.com/photo-1526367790999-0150786686a2?w=200&auto=format&fit=crop&q=80',
        category_id: 'cat_startup',
        amount: 18500,
        currency: 'BDT',
        gateway: 'nagad',
        mode: 'live',
        status: 'paid',
        created_at: '2026-08-24T15:20:00Z',
        updated_at: '2026-08-24T15:20:03Z',
        paid_at: '2026-08-24T15:20:03Z',
        callback_processed_at: '2026-08-24T15:20:03Z',
      },
    ];

    demo.forEach(t => this.transactions.set(t.transaction_id, t));
  }

  // --- Transactions CRUD (Persisted to Database) ---

  public createTransaction(txn: ServerTransaction): ServerTransaction {
    this.transactions.set(txn.transaction_id, { ...txn });
    this.saveTransactionsToDisk();
    return { ...txn };
  }

  public getTransaction(transactionId: string): ServerTransaction | undefined {
    const txn = this.transactions.get(transactionId);
    return txn ? { ...txn } : undefined;
  }

  public getTransactionByGatewayTxnId(gatewayTxnId: string): ServerTransaction | undefined {
    for (const txn of this.transactions.values()) {
      if (txn.gateway_transaction_id === gatewayTxnId) {
        return { ...txn };
      }
    }
    return undefined;
  }

  public getAllTransactions(): ServerTransaction[] {
    return Array.from(this.transactions.values()).sort(
      (a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()
    );
  }

  public getTransactionsByListingId(listingId: string): ServerTransaction[] {
    return Array.from(this.transactions.values())
      .filter(t => t.listing_id === listingId)
      .sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());
  }

  public getTransactionsByStatus(status: PaymentStatus): ServerTransaction[] {
    return Array.from(this.transactions.values())
      .filter(t => t.status === status)
      .sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());
  }

  /**
   * Idempotent status update:
   * Returns true if status was updated, false if already in final state (PAID/FAILED)
   */
  public updateTransactionStatus(
    transactionId: string,
    status: PaymentStatus,
    extra?: Partial<ServerTransaction>
  ): { updated: boolean; transaction?: ServerTransaction } {
    const txn = this.transactions.get(transactionId);
    if (!txn) {
      return { updated: false };
    }

    // Idempotency: if already marked paid, avoid duplicate activation/processing
    if (txn.status === 'paid' && status === 'paid') {
      return { updated: false, transaction: { ...txn } };
    }

    const now = new Date().toISOString();
    const updatedTxn: ServerTransaction = {
      ...txn,
      ...extra,
      status,
      updated_at: now,
      paid_at: status === 'paid' ? (extra?.paid_at || now) : txn.paid_at,
      callback_processed_at: now,
    };

    this.transactions.set(transactionId, updatedTxn);
    this.saveTransactionsToDisk();
    return { updated: true, transaction: { ...updatedTxn } };
  }

  public deleteTransaction(transactionId: string): boolean {
    const deleted = this.transactions.delete(transactionId);
    if (deleted) {
      this.saveTransactionsToDisk();
    }
    return deleted;
  }

  // --- Gateway Settings & Secrets ---

  private loadGatewaysFromDisk(): void {
    try {
      if (fs.existsSync(this.gatewaysFilePath)) {
        const raw = fs.readFileSync(this.gatewaysFilePath, 'utf-8');
        const data = JSON.parse(raw);
        if (Array.isArray(data.gateways)) {
          data.gateways.forEach((gw: GatewaySecretConfig) => {
            const existing = this.gatewayConfigs.get(gw.id);
            this.gatewayConfigs.set(gw.id, { ...existing, ...gw });
          });
        }
        if (data.defaultGateway) {
          this.defaultGateway = data.defaultGateway;
        }
      }
    } catch (e) {
      console.warn('Could not load gateway config from disk, using defaults:', e);
    }
  }

  private saveGatewaysToDisk(): void {
    try {
      const dir = path.dirname(this.gatewaysFilePath);
      if (!fs.existsSync(dir)) {
        fs.mkdirSync(dir, { recursive: true });
      }
      const data = {
        gateways: Array.from(this.gatewayConfigs.values()),
        defaultGateway: this.defaultGateway,
        updatedAt: new Date().toISOString(),
      };
      fs.writeFileSync(this.gatewaysFilePath, JSON.stringify(data, null, 2), 'utf-8');
    } catch (e) {
      console.error('Failed to persist gateway configs to disk:', e);
    }
  }

  public getGatewayConfig(gatewayId: string): GatewaySecretConfig | undefined {
    const gw = this.gatewayConfigs.get(gatewayId);
    return gw ? { ...gw } : undefined;
  }

  public getAllGatewayConfigs(maskSecrets: boolean = true): GatewaySecretConfig[] {
    return Array.from(this.gatewayConfigs.values()).map(gw => {
      if (!maskSecrets) return { ...gw };
      return this.maskConfig(gw);
    });
  }

  public getPublicGateways(): Array<{ id: string; name: string; is_enabled: boolean; mode: 'test' | 'live'; allowed_channels?: string[] }> {
    return Array.from(this.gatewayConfigs.values())
      .filter(gw => gw.is_enabled)
      .map(gw => ({
        id: gw.id,
        name: gw.name,
        is_enabled: gw.is_enabled,
        mode: gw.mode,
        allowed_channels: gw.allowed_channels,
      }));
  }

  public saveGatewayConfig(updated: GatewaySecretConfig): void {
    const existing = this.gatewayConfigs.get(updated.id);
    if (!existing) {
      this.gatewayConfigs.set(updated.id, { ...updated });
      this.saveGatewaysToDisk();
      return;
    }

    // Retain existing secret values if admin submitted masked placeholder
    const merged: GatewaySecretConfig = {
      ...existing,
      ...updated,
      api_key: this.unmaskValue(updated.api_key, existing.api_key),
      app_key: this.unmaskValue(updated.app_key, existing.app_key),
      app_secret: this.unmaskValue(updated.app_secret, existing.app_secret),
      password: this.unmaskValue(updated.password, existing.password),
      store_password: this.unmaskValue(updated.store_password, existing.store_password),
      signature_key: this.unmaskValue(updated.signature_key, existing.signature_key),
      private_key: this.unmaskValue(updated.private_key, existing.private_key),
      pin_password: this.unmaskValue(updated.pin_password, existing.pin_password),
      allowed_channels: updated.allowed_channels || existing.allowed_channels,
    };

    this.gatewayConfigs.set(updated.id, merged);
    this.saveGatewaysToDisk();
  }

  public saveAllGatewayConfigs(configs: GatewaySecretConfig[]): void {
    configs.forEach(c => {
      const existing = this.gatewayConfigs.get(c.id);
      if (!existing) {
        this.gatewayConfigs.set(c.id, { ...c });
      } else {
        const merged: GatewaySecretConfig = {
          ...existing,
          ...c,
          api_key: this.unmaskValue(c.api_key, existing.api_key),
          app_key: this.unmaskValue(c.app_key, existing.app_key),
          app_secret: this.unmaskValue(c.app_secret, existing.app_secret),
          password: this.unmaskValue(c.password, existing.password),
          store_password: this.unmaskValue(c.store_password, existing.store_password),
          signature_key: this.unmaskValue(c.signature_key, existing.signature_key),
          private_key: this.unmaskValue(c.private_key, existing.private_key),
          pin_password: this.unmaskValue(c.pin_password, existing.pin_password),
          allowed_channels: c.allowed_channels || existing.allowed_channels,
        };
        this.gatewayConfigs.set(c.id, merged);
      }
    });
    this.saveGatewaysToDisk();
  }

  public getDefaultGateway(): string {
    return this.defaultGateway;
  }

  public setDefaultGateway(gwId: string): void {
    this.defaultGateway = gwId;
    this.saveGatewaysToDisk();
  }

  private maskConfig(gw: GatewaySecretConfig): GatewaySecretConfig {
    return {
      ...gw,
      api_key: gw.api_key ? '••••••••••••••••' : '',
      app_secret: gw.app_secret ? '••••••••••••••••' : '',
      password: gw.password ? '••••••••••••••••' : '',
      store_password: gw.store_password ? '••••••••••••••••' : '',
      signature_key: gw.signature_key ? '••••••••••••••••' : '',
      private_key: gw.private_key ? '••••••••••••••••' : '',
      pin_password: gw.pin_password ? '••••••••••••••••' : '',
    };
  }

  private unmaskValue(submitted?: string, existing?: string): string | undefined {
    if (!submitted || submitted.includes('••••')) {
      return existing;
    }
    return submitted;
  }
}

export const paymentStore = new PaymentStore();
