/**
 * Products registry REST endpoints.
 *
 * GET    /products       — list all products (public, cached)
 * GET    /products/:id   — get one product (public, cached)
 * POST   /products       — create product (admin-only)
 * PUT    /products/:id   — update product (admin-only, refreshes cache)
 */

import type { FastifyInstance } from 'fastify';
import { BadRequestError, ConflictError, NotFoundError } from '../../lib/errors.js';
import { extractAuth } from '../../lib/auth.js';
import * as repo from './repository.js';
import { loadProductCache, getAllProducts, getProduct, productsForUser } from './cache.js';
import {
  CreateProductSchema,
  UpdateProductSchema,
  isValidStatusTransition,
  type ProductDoc,
  type ProductStatus,
} from './types.js';

export async function productRoutes(app: FastifyInstance) {
  // List all products (served from cache)
  app.get('/products', async () => {
    return { products: getAllProducts() };
  });

  // List the authenticated caller's products ("my projects"). Owner-scoped:
  // a normal user sees the products they own; admins/super_admins see them all.
  // This is the per-user, multi-tenant view the dashboard switcher consumes.
  app.get('/products/mine', async req => {
    const auth = await extractAuth(req);
    const isAdmin = auth.role === 'admin' || auth.role === 'super_admin';
    return { products: productsForUser(auth.sub, isAdmin) };
  });

  // Get single product (served from cache)
  app.get('/products/:id', async req => {
    const { id } = req.params as { id: string };
    const product = getProduct(id);
    if (!product) throw new NotFoundError('Product not found');
    return product;
  });

  // Create product (admin-only)
  app.post('/products', async (req, reply) => {
    const auth = await extractAuth(req);
    const parsed = CreateProductSchema.safeParse(req.body);
    if (!parsed.success) {
      throw new BadRequestError(parsed.error.issues.map(i => i.message).join('; '));
    }
    const input = parsed.data;

    // Check for duplicate
    const existing = await repo.getById(input.productId);
    if (existing) throw new ConflictError(`Product "${input.productId}" already exists`);

    const now = new Date().toISOString();
    const doc: ProductDoc = {
      id: input.productId,
      ...input,
      // Attribute ownership to the creator (unless explicitly supplied) so it
      // shows up under "my projects".
      ownerId: input.ownerId ?? auth.sub,
      createdAt: now,
      updatedAt: now,
    };

    const created = await repo.create(doc);
    await loadProductCache();
    reply.code(201);
    return created;
  });

  // Update product (admin-only, refreshes cache)
  app.put('/products/:id', async req => {
    const { id } = req.params as { id: string };
    const existing = await repo.getById(id);
    if (!existing) throw new NotFoundError('Product not found');

    const parsed = UpdateProductSchema.safeParse(req.body);
    if (!parsed.success) {
      throw new BadRequestError(parsed.error.issues.map(i => i.message).join('; '));
    }

    // Validate status transition if status is changing
    if (parsed.data.status && parsed.data.status !== existing.status) {
      if (!isValidStatusTransition(existing.status, parsed.data.status as ProductStatus)) {
        throw new BadRequestError(
          `Invalid status transition: ${existing.status} → ${parsed.data.status}`
        );
      }
    }

    // Merge deviceLimits if partial update provided
    const { deviceLimits, prelaunchConfig, ...rest } = parsed.data;
    const updates: Partial<ProductDoc> = { ...rest };
    if (deviceLimits) {
      updates.deviceLimits = { ...existing.deviceLimits, ...deviceLimits };
    }
    // Merge prelaunchConfig if partial update provided
    if (prelaunchConfig) {
      const base = existing.prelaunchConfig ?? {};
      updates.prelaunchConfig = { ...base, ...prelaunchConfig } as ProductDoc['prelaunchConfig'];
    }

    const updated = await repo.update(id, updates);
    if (!updated) throw new NotFoundError('Product update failed');
    await loadProductCache();
    return updated;
  });
}
