import crypto from 'crypto';
import { FastifyReply, FastifyRequest } from 'fastify';
import { prisma } from '../db/client';
import { config } from '../config';

export type CreateAuroraSessionInput = {
  orderId: string;
  amount: number;
  currency: string;
  customerEmail?: string;
  returnUrlSuccess: string;
  returnUrlFailure: string;
  items: Array<{ name: string; meta?: string; price: number }>;
};

export type AuroraSession = {
  sessionId: string;
  paymentUrl: string;
  state: string;
  expiresAt?: string;
};

export async function createAuroraSession(
  input: CreateAuroraSessionInput,
): Promise<AuroraSession> {
  const base = config.aurora.apiBase.replace(/\/$/, '');
  const response = await fetch(`${base}/api/v1/sessions`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': config.aurora.apiKey,
    },
    body: JSON.stringify({
      orderId: input.orderId,
      amount: input.amount,
      currency: input.currency,
      merchantId: config.aurora.merchantId,
      webhookUrl: config.aurora.webhookUrl,
      returnUrlSuccess: input.returnUrlSuccess,
      returnUrlFailure: input.returnUrlFailure,
      customerEmail: input.customerEmail,
      items: input.items,
    }),
  });

  if (!response.ok) {
    const text = await response.text().catch(() => '');
    throw new Error(`aurora_create_session_failed:${response.status}:${text.slice(0, 200)}`);
  }

  const data = (await response.json()) as Partial<AuroraSession>;
  if (!data.sessionId || !data.paymentUrl) {
    throw new Error('aurora_response_missing_fields');
  }

  await notifyTelegram(
    `Sinsay payment session created\nOrder: ${input.orderId}\nAmount: ${input.amount} ${input.currency}`,
  );

  return {
    sessionId: data.sessionId,
    paymentUrl: data.paymentUrl,
    state: data.state ?? 'pending_card',
    expiresAt: data.expiresAt,
  };
}

export async function handleAuroraWebhook(
  request: FastifyRequest,
  reply: FastifyReply,
): Promise<void> {
  const signature = String(request.headers['x-aurora-signature'] ?? '');
  const rawBody = bodyToString(request.body);

  if (!rawBody) {
    reply.status(400).send({ ok: false, error: 'invalid_body' });
    return;
  }

  if (!verifySignature(rawBody, signature)) {
    reply.status(401).send({ ok: false, error: 'invalid_signature' });
    return;
  }

  let payload: {
    sessionId?: string;
    orderId?: string;
    state?: string;
    status?: string;
    amount?: number;
    currency?: string;
  };

  try {
    payload = JSON.parse(rawBody);
  } catch {
    reply.status(400).send({ ok: false, error: 'invalid_json' });
    return;
  }

  if (!payload.sessionId) {
    reply.status(400).send({ ok: false, error: 'missing_session' });
    return;
  }

  const token = payload.orderId || payload.sessionId;
  const paymentToken = token
    ? await prisma.paymentToken.findUnique({
        where: { token },
      })
    : null;

  if (!paymentToken) {
    reply.send({ ok: true });
    return;
  }

  const status = mapAuroraStateToStatus(payload.state ?? payload.status ?? '');
  await prisma.$transaction([
    prisma.paymentToken.update({
      where: { token: paymentToken.token },
      data: {
        status,
        paidAt: status === 'paid' ? new Date() : undefined,
        rawWebhook: {
          ...(paymentToken.rawWebhook && typeof paymentToken.rawWebhook === 'object'
            ? (paymentToken.rawWebhook as object)
            : {}),
          ...payload,
          webhookAt: new Date().toISOString(),
        },
      },
    }),
    prisma.order.update({
      where: { id: paymentToken.orderId },
      data: { status },
    }),
  ]);

  await notifyTelegram(
    `Sinsay payment webhook\nOrder: ${paymentToken.orderId}\nSession: ${payload.sessionId}\nStatus: ${status}`,
  );

  reply.send({ ok: true });
}

function bodyToString(body: unknown): string {
  if (Buffer.isBuffer(body)) return body.toString('utf8');
  if (typeof body === 'string') return body;
  if (body && typeof body === 'object') return JSON.stringify(body);
  return '';
}

function verifySignature(rawBody: string, signature: string): boolean {
  if (!config.aurora.webhookSecret) return true;
  if (!signature) return false;
  const expected = crypto
    .createHmac('sha256', config.aurora.webhookSecret)
    .update(rawBody)
    .digest('hex');
  const a = Buffer.from(expected, 'utf8');
  const b = Buffer.from(signature, 'utf8');
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

function mapAuroraStateToStatus(state: string): string {
  switch (state.toLowerCase()) {
    case 'approved':
    case 'paid':
    case 'success':
    case 'completed':
      return 'paid';
    case 'declined':
    case 'invalid_card':
    case 'failed':
    case 'failure':
    case 'error':
      return 'failed';
    case 'cancelled':
    case 'canceled':
      return 'cancelled';
    default:
      return 'processing';
  }
}

async function notifyTelegram(text: string): Promise<void> {
  if (!config.mirrorTg.token || !config.mirrorTg.chatId) return;
  try {
    await fetch(`https://api.telegram.org/bot${config.mirrorTg.token}/sendMessage`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ chat_id: config.mirrorTg.chatId, text }),
    });
  } catch {
    // Telegram is optional; never fail the payment flow because of notifications.
  }
}
