import jwt from 'jsonwebtoken';

import type {
  BillingWebhookHandlerService,
  IHandleWebhookEventParams,
} from '@kit/billing';
import { getLogger } from '@kit/shared/logger';
import { Database, Enums } from '@kit/supabase/database';

import { MontonioServerEnvSchema } from '../schema/montonio-server-env.schema';

type UpsertOrderParams =
  Database['medreport']['Functions']['upsert_order']['Args'];

type BillingProvider = Enums<{ schema: 'medreport' }, 'billing_provider'>;

interface MontonioOrderToken {
  uuid: string;
  accessKey: string;
  merchantReference: string;
  merchantReferenceDisplay: string;
  paymentStatus:
    | 'PAID'
    | 'FAILED'
    | 'CANCELLED'
    | 'PENDING'
    | 'EXPIRED'
    | 'REFUNDED';
  paymentMethod: string;
  grandTotal: number;
  currency: string;
  senderIban?: string;
  senderName?: string;
  paymentProviderName?: string;
  paymentLinkUuid: string;
  iat: number;
  exp: number;
}

const env = () =>
  MontonioServerEnvSchema.parse({
    apiUrl: process.env.MONTONIO_API_URL,
    secretKey: process.env.MONTONIO_SECRET_KEY,
  });

export class MontonioWebhookHandlerService
  implements BillingWebhookHandlerService
{
  private readonly provider: BillingProvider = 'montonio';
  private readonly namespace = 'billing.montonio';

  async verifyWebhookSignature(request: Request) {
    const logger = await getLogger();
    const { secretKey } = env();

    let token: string;
    try {
      const url = new URL(request.url);
      const searchParams = url.searchParams;
      console.info('searchParams', searchParams, url);
      const tokenParam = searchParams.get('order-token') as string | null;
      if (!tokenParam) {
        throw new Error('Missing order-token');
      }
      token = tokenParam;
    } catch (error) {
      logger.error(
        {
          error,
          name: this.namespace,
        },
        `Failed to parse Montonio webhook request`,
      );
      throw new Error('Invalid request');
    }

    try {
      const decoded = jwt.verify(token, secretKey, {
        algorithms: ['HS256'],
      });
      return decoded as MontonioOrderToken;
    } catch (error) {
      logger.error(
        {
          error,
          name: this.namespace,
        },
        `Failed to verify Montonio webhook signature`,
      );
      throw new Error('Invalid signature');
    }
  }

  async handleWebhookEvent(
    event: MontonioOrderToken,
    params: IHandleWebhookEventParams,
  ) {
    const logger = await getLogger();

    logger.info(
      {
        name: this.namespace,
        event,
      },
      `Received Montonio webhook event`,
    );

    if (event.paymentStatus === 'PAID') {
      const [accountId] = event.merchantReferenceDisplay.split(':');
      if (!accountId) {
        throw new Error('Invalid merchant reference');
      }
      const order: UpsertOrderParams = {
        target_account_id: accountId,
        target_customer_id: '',
        target_order_id: event.uuid,
        status: 'succeeded',
        billing_provider: this.provider,
        total_amount: event.grandTotal,
        currency: event.currency,
        line_items: [],
      };
      return params.onCheckoutSessionCompleted(order);
    }

    if (
      event.paymentStatus === 'FAILED' ||
      event.paymentStatus === 'CANCELLED'
    ) {
      return params.onPaymentFailed(event.uuid);
    }

    return;
  }
}
