import type { FastifyInstance } from "fastify";
import { eq } from "drizzle-orm";
import { schema } from "@app/database";
import { verifyPurchaseInputSchema } from "@app/validation";
import { errors, parse } from "../lib/errors.js";
import { audit, emitEvent, getActiveHouseholdId } from "../lib/helpers.js";
import { loadEntitlementsWithToken } from "../lib/entitlements.js";

/**
 * Prenumerationer (spec §45–47, Del 13).
 * Backend verifierar ALLTID mot butiken och är source of truth (spec §61.14).
 * Webhooks tas emot råa och processas asynkront av workern.
 */
export async function subscriptionRoutes(app: FastifyInstance) {
  const auth = { preHandler: [app.authenticate] };

  app.post("/v1/subscriptions/verify", auth, async (req) => {
    const input = parse(verifyPurchaseInputSchema, req.body);

    const result =
      input.provider === "apple"
        ? await app.storeVerifier.verifyApple(input.signedTransaction)
        : await app.storeVerifier.verifyGoogle(
            input.packageName,
            input.productId,
            input.purchaseToken,
          );

    if (!result.ok) throw errors.badRequest(`Kunde inte verifiera köpet: ${result.error}`);
    const purchase = result.purchase;
    const householdId = await getActiveHouseholdId(app.db, req.userId);

    // Idempotent på originalTransactionId
    const [existing] = await app.db
      .select()
      .from(schema.subscriptions)
      .where(eq(schema.subscriptions.originalTransactionId, purchase.originalTransactionId))
      .limit(1);

    let subscriptionId: string;
    if (existing) {
      if (existing.userId !== req.userId) {
        throw errors.conflict(
          "Det här köpet är kopplat till ett annat konto. Använd Återställ köp på rätt konto.",
        );
      }
      const [updated] = await app.db
        .update(schema.subscriptions)
        .set({
          status: purchase.status,
          expiresAt: purchase.expiresAt,
          plan: purchase.plan,
          productId: purchase.productId,
          lastVerifiedAt: new Date(),
          updatedAt: new Date(),
        })
        .where(eq(schema.subscriptions.id, existing.id))
        .returning();
      subscriptionId = updated!.id;
    } else {
      const [created] = await app.db
        .insert(schema.subscriptions)
        .values({
          userId: req.userId,
          householdId,
          provider: purchase.provider,
          productId: purchase.productId,
          plan: purchase.plan,
          originalTransactionId: purchase.originalTransactionId,
          status: purchase.status,
          purchasedAt: purchase.purchasedAt,
          expiresAt: purchase.expiresAt,
          lastVerifiedAt: new Date(),
        })
        .returning();
      subscriptionId = created!.id;
      await emitEvent(app.db, {
        type: "SUBSCRIPTION_STARTED",
        payload: { subscriptionId, plan: purchase.plan, provider: purchase.provider },
        userId: req.userId,
        householdId: householdId ?? undefined,
        correlationId: req.correlationId,
      });
    }

    await app.db.insert(schema.subscriptionEvents).values({
      subscriptionId,
      userId: req.userId,
      eventType: existing ? "verified" : "purchased",
      payload: { productId: purchase.productId },
    });
    await audit(app.db, {
      actorUserId: req.userId,
      action: "subscription.verified",
      targetType: "subscription",
      targetId: subscriptionId,
    });

    return { ok: true, entitlements: await loadEntitlementsWithToken(app, req.userId) };
  });

  app.post("/v1/subscriptions/restore", auth, async (req) => {
    // Restore = samma flöde som verify; klienten skickar aktuellt kvitto/token.
    return {
      ok: true,
      message: "Skicka aktuellt kvitto till /v1/subscriptions/verify så återställs köpet.",
    };
  });

  /**
   * App Store Server Notifications V2 (spec §47).
   * Signaturverifiering av JWS sker i workern (PROCESS_STORE_NOTIFICATION).
   */
  app.post(
    "/v1/subscriptions/webhooks/apple",
    { config: { rateLimit: false } },
    async (req, reply) => {
      const [row] = await app.db
        .insert(schema.storeNotifications)
        .values({ provider: "apple", rawPayload: (req.body ?? {}) as Record<string, unknown> })
        .returning();
      await app.jobQueue.add("PROCESS_STORE_NOTIFICATION", {
        jobType: "PROCESS_STORE_NOTIFICATION",
        notificationId: row!.id,
      });
      return reply.status(200).send({ ok: true });
    },
  );

  /** Google Play Real-time Developer Notifications (via Pub/Sub push). */
  app.post(
    "/v1/subscriptions/webhooks/google",
    { config: { rateLimit: false } },
    async (req, reply) => {
      const [row] = await app.db
        .insert(schema.storeNotifications)
        .values({ provider: "google", rawPayload: (req.body ?? {}) as Record<string, unknown> })
        .returning();
      await app.jobQueue.add("PROCESS_STORE_NOTIFICATION", {
        jobType: "PROCESS_STORE_NOTIFICATION",
        notificationId: row!.id,
      });
      return reply.status(200).send({ ok: true });
    },
  );
}
