/**
 * billing-svc — тарифы, подписки, ЮKassa
 * docs/architecture: 01-perplexity-analogue-design.md §2.2.J, 02-k3s-microservices-spec.md §3.10
 * API: GET /api/v1/billing/plans, /subscription, /payments; POST /checkout
 */

import Fastify from 'fastify';
import cors from '@fastify/cors';
import { desc, eq } from 'drizzle-orm';
import { db } from './db/index.js';
import { plans as plansTable, payments, subscriptions } from './db/schema.js';
import { PLANS } from './plans.js';

const PORT = parseInt(process.env.PORT ?? '3008', 10);
const AUTH_SERVICE_URL = process.env.AUTH_SERVICE_URL ?? '';
const YOOKASSA_SHOP_ID = process.env.YOOKASSA_SHOP_ID ?? '';
const YOOKASSA_SECRET = process.env.YOOKASSA_SECRET ?? '';
const RETURN_URL =
  process.env.BILLING_RETURN_URL ?? 'http://localhost:3000/profile?tab=billing';

async function getUserIdFromToken(authHeader: string | undefined): Promise<string | null> {
  if (!authHeader?.startsWith('Bearer ')) return null;
  const token = authHeader.slice(7);
  if (!AUTH_SERVICE_URL || !token) return null;
  try {
    const res = await fetch(
      `${AUTH_SERVICE_URL.replace(/\/$/, '')}/api/auth/me`,
      {
        headers: { Authorization: `Bearer ${token}` },
        signal: AbortSignal.timeout(5000),
      }
    );
    if (!res.ok) return null;
    const data = (await res.json()) as { id?: string };
    return data.id ?? null;
  } catch {
    return null;
  }
}

const app = Fastify({ logger: true });

await app.register(cors, { origin: true });

app.get('/health', async () => ({ status: 'ok', service: 'billing-svc' }));
app.get('/ready', async () => ({ status: 'ready' }));

// Тарифы — публично, без авторизации
const plansResponse = PLANS.map((p) => ({
  id: p.id,
  name: p.name,
  priceMonthly: p.priceMonthly,
  priceYearly: p.priceYearly,
  currency: p.currency,
  features: p.features ?? [],
  limits: p.limits ?? {},
}));

app.get('/api/v1/billing/plans', async (_req, reply) => {
  try {
    const existing = await db.select().from(plansTable);
    if (existing.length > 0) {
      return reply.send(
        existing.map((p) => ({
          id: p.id,
          name: p.name,
          priceMonthly: p.priceMonthly,
          priceYearly: p.priceYearly,
          currency: p.currency,
          features: p.features ?? [],
          limits: p.limits ?? {},
        }))
      );
    }
    try {
      await db.insert(plansTable).values(PLANS);
    } catch {
      // Таблица может отсутствовать — возвращаем статичные планы
    }
    return reply.send(plansResponse);
  } catch {
    return reply.send(plansResponse);
  }
});

// Текущая подписка пользователя
app.get('/api/v1/billing/subscription', async (req, reply) => {
  const userId = await getUserIdFromToken(req.headers.authorization);
  if (!userId) {
    return reply.status(401).send({ error: 'Authorization required' });
  }
  try {
    const [sub] = await db
      .select()
      .from(subscriptions)
      .where(eq(subscriptions.userId, userId))
      .orderBy(desc(subscriptions.createdAt))
      .limit(1);
    if (!sub) {
      return reply.send({
        planId: 'free',
        status: 'active',
        period: 'monthly',
        startedAt: null,
        expiresAt: null,
      });
    }
    return reply.send({
      id: sub.id,
      planId: sub.planId,
      status: sub.status,
      period: sub.period,
      startedAt: sub.startedAt?.toISOString?.() ?? sub.startedAt,
      expiresAt: sub.expiresAt?.toISOString?.() ?? sub.expiresAt,
    });
  } catch (err) {
    app.log.error(err);
    return reply.status(500).send({ error: 'Failed to fetch subscription' });
  }
});

// История платежей
app.get('/api/v1/billing/payments', async (req, reply) => {
  const userId = await getUserIdFromToken(req.headers.authorization);
  if (!userId) {
    return reply.status(401).send({ error: 'Authorization required' });
  }
  try {
    const list = await db
      .select()
      .from(payments)
      .where(eq(payments.userId, userId))
      .orderBy(desc(payments.createdAt))
      .limit(50);
    return reply.send({
      payments: list.map((p) => ({
        id: p.id,
        planId: p.planId,
        amount: p.amount,
        currency: p.currency,
        status: p.status,
        period: p.period,
        createdAt: p.createdAt?.toISOString?.() ?? p.createdAt,
      })),
    });
  } catch (err) {
    app.log.error(err);
    return reply.status(500).send({ error: 'Failed to fetch payments' });
  }
});

// Создать платёж (checkout)
app.post<{
  Body: { planId: string; period?: 'monthly' | 'yearly' };
}>('/api/v1/billing/checkout', async (req, reply) => {
  const userId = await getUserIdFromToken(req.headers.authorization);
  if (!userId) {
    return reply.status(401).send({ error: 'Authorization required' });
  }
  const { planId, period = 'monthly' } = req.body ?? {};
  if (!planId || !['free', 'pro', 'max'].includes(planId)) {
    return reply.status(400).send({ error: 'Invalid planId' });
  }
  if (planId === 'free') {
    return reply.status(400).send({ error: 'Free plan does not require payment' });
  }

  const plan = PLANS.find((p) => p.id === planId);
  if (!plan) {
    return reply.status(400).send({ error: 'Plan not found' });
  }

  const amount: number =
    period === 'yearly'
      ? (plan.priceYearly ?? 0)
      : (plan.priceMonthly ?? 0);
  if (amount <= 0) {
    return reply.status(400).send({ error: 'Plan is free' });
  }

  const paymentId = crypto.randomUUID();

  if (YOOKASSA_SHOP_ID && YOOKASSA_SECRET) {
    try {
      const auth = Buffer.from(`${YOOKASSA_SHOP_ID}:${YOOKASSA_SECRET}`).toString('base64');
      const res = await fetch('https://api.yookassa.ru/v3/payments', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          Authorization: `Basic ${auth}`,
          'Idempotence-Key': paymentId,
        },
        body: JSON.stringify({
          amount: {
            value: (amount / 100).toFixed(2),
            currency: plan.currency,
          },
          capture: true,
          confirmation: {
            type: 'redirect',
            return_url: RETURN_URL,
          },
          description: `GooSeek ${plan.name} (${period})`,
          metadata: { userId, planId, period },
        }),
        signal: AbortSignal.timeout(15000),
      });

      if (!res.ok) {
        const errBody = await res.text();
        app.log.warn({ status: res.status, body: errBody }, 'YooKassa error');
        return reply.status(502).send({
          error: 'Payment provider error',
          details: await res.json().catch(() => ({})),
        });
      }

      const data = (await res.json()) as {
        id?: string;
        confirmation?: { confirmation_url?: string };
        status?: string;
      };

      await db.insert(payments).values({
        id: paymentId,
        userId,
        planId,
        amount,
        currency: plan.currency,
        status: data.status ?? 'pending',
        yookassaId: data.id ?? null,
        period,
      });

      return reply.send({
        paymentId,
        redirectUrl: data.confirmation?.confirmation_url ?? null,
        status: data.status,
      });
    } catch (err) {
      app.log.error(err);
      return reply.status(502).send({ error: 'Payment provider unavailable' });
    }
  }

  // Mock mode — без ЮKassa возвращаем тестовую ссылку
  await db.insert(payments).values({
    id: paymentId,
    userId,
    planId,
    amount,
    currency: plan.currency ?? 'RUB',
    status: 'pending',
    yookassaId: null,
    period,
  });

  return reply.send({
    paymentId,
    redirectUrl: null,
    status: 'pending',
    message: 'YooKassa not configured. Set YOOKASSA_SHOP_ID and YOOKASSA_SECRET.',
  });
});

// Webhook ЮKassa — подтверждение платежа (POST от ЮKassa)
app.post<{ Body: { object?: { id?: string; status?: string; metadata?: Record<string, string> } } }>(
  '/api/v1/billing/webhook',
  async (req, reply) => {
    const body = req.body as { object?: { id?: string; status?: string; metadata?: Record<string, string> } };
    const obj = body?.object;
    if (!obj?.metadata?.userId || !obj?.metadata?.planId) {
      return reply.status(400).send({ error: 'Invalid webhook payload' });
    }
    if (obj.status !== 'succeeded') {
      return reply.send({ received: true });
    }
    try {
      const [payment] = await db
        .select()
        .from(payments)
        .where(eq(payments.yookassaId, obj.id ?? ''));
      if (!payment) {
        return reply.send({ received: true });
      }
      await db
        .update(payments)
        .set({ status: 'succeeded' })
        .where(eq(payments.id, payment.id));

      const expiresAt = new Date();
      expiresAt.setMonth(expiresAt.getMonth() + (payment.period === 'yearly' ? 12 : 1));

      await db.insert(subscriptions).values({
        id: crypto.randomUUID(),
        userId: payment.userId,
        planId: payment.planId,
        status: 'active',
        period: payment.period,
        startedAt: new Date(),
        expiresAt,
      });
      return reply.send({ received: true });
    } catch (err) {
      app.log.error(err);
      return reply.status(500).send({ error: 'Webhook processing failed' });
    }
  }
);

app.listen({ port: PORT, host: '0.0.0.0' }, (err) => {
  if (err) {
    app.log.error(err);
    process.exit(1);
  }
  console.log(`billing-svc listening on :${PORT}`);
  console.log('  GET /api/v1/billing/plans — tariff plans');
  console.log('  GET /api/v1/billing/subscription — current subscription (Bearer)');
  console.log('  GET /api/v1/billing/payments — payment history (Bearer)');
  console.log('  POST /api/v1/billing/checkout — create payment (Bearer)');
});
