'use server';

import { redirect } from 'next/navigation';
import { prisma } from '@/lib/prisma';
import { DEFAULT_PRINT_AREAS } from '@/lib/mockupDefaults';

function slugify(input: string) {
  return input
    .toLowerCase()
    .trim()
    .replace(/[^a-z0-9]+/g, '-')
    .replace(/^-+|-+$/g, '');
}

async function uniqueSlug(base: string) {
  let slug = base || 'product';
  let n = 2;
  while (await prisma.product.findUnique({ where: { slug } })) {
    slug = `${base}-${n}`;
    n += 1;
  }
  return slug;
}

async function fileToDataUrl(file: File): Promise<string> {
  const bytes = Buffer.from(await file.arrayBuffer());
  const mimeType = file.type || 'image/png';
  return `data:${mimeType};base64,${bytes.toString('base64')}`;
}

// Reads the per-color photo file inputs the ColorPicker renders (named
// "colorPhoto__<hexWithoutHash>" for front, "colorPhotoBack__<hexWithoutHash>" for
// back) and builds { hex: dataUrl } for whichever colors actually got a file attached.
async function extractColorPhotos(
  formData: FormData,
  colors: string[],
  variant: 'front' | 'back' = 'front',
): Promise<Record<string, string>> {
  const prefix = variant === 'front' ? 'colorPhoto__' : 'colorPhotoBack__';
  const result: Record<string, string> = {};
  for (const hex of colors) {
    const file = formData.get(`${prefix}${hex.replace('#', '')}`) as File | null;
    if (file && file.size > 0) {
      result[hex] = await fileToDataUrl(file);
    }
  }
  return result;
}

// Shared by createProduct/updateProduct — empty inputs mean "no sale". The end
// date is pushed to the end of that day so the sale stays active through it.
function parseSaleFields(formData: FormData) {
  const salePriceInput = String(formData.get('salePrice') ?? '').trim();
  const startInput = String(formData.get('saleStartsAt') ?? '').trim();
  const endInput = String(formData.get('saleEndsAt') ?? '').trim();

  return {
    salePrice: salePriceInput ? Math.round(Number(salePriceInput) * 100) : null,
    saleStartsAt: startInput ? new Date(startInput) : null,
    saleEndsAt: endInput ? new Date(`${endInput}T23:59:59`) : null,
  };
}

export async function createProduct(formData: FormData) {
  const name = String(formData.get('name') ?? '').trim();
  const slugInput = String(formData.get('slug') ?? '').trim();
  const category = String(formData.get('category') ?? '');
  const description = String(formData.get('description') ?? '').trim();
  const priceDollars = Number(formData.get('price') ?? 0);
  const personalisedPriceDollars = String(formData.get('personalisedPrice') ?? '').trim();
  const mockup = String(formData.get('mockup') ?? 'tshirt');
  const colorsInput = String(formData.get('colors') ?? '');
  const sizesInput = String(formData.get('sizes') ?? '');
  const imageFile = formData.get('image') as File | null;
  const imageBackFile = formData.get('imageBack') as File | null;
  const photoRecolorable = String(formData.get('photoRecolorable') ?? '') === '1';
  const showOnPersonalised = String(formData.get('showOnPersonalised') ?? '') === '1';
  const showOnProducts = String(formData.get('showOnProducts') ?? '') === '1';
  const collectionIds = formData.getAll('collections').map((v) => String(v));

  if (!name || !category || !priceDollars) {
    throw new Error('Name, category, and price are required.');
  }

  const colors = colorsInput
    .split(',')
    .map((c) => c.trim())
    .filter(Boolean);

  if (colors.length === 0) {
    throw new Error('At least one color is required.');
  }

  const sizes = sizesInput
    .split(',')
    .map((s) => s.trim())
    .filter(Boolean);

  const imageUrl = imageFile && imageFile.size > 0 ? await fileToDataUrl(imageFile) : null;
  const imageUrlBack = imageBackFile && imageBackFile.size > 0 ? await fileToDataUrl(imageBackFile) : null;
  const colorPhotos = await extractColorPhotos(formData, colors, 'front');
  const colorPhotosBack = await extractColorPhotos(formData, colors, 'back');
  const saleFields = parseSaleFields(formData);

  const slug = await uniqueSlug(slugify(slugInput || name));

  const hasCustomPrintArea = String(formData.get('hasCustomPrintArea') ?? '') === '1' && imageUrl;
  const printArea = hasCustomPrintArea
    ? {
        xPct: Number(formData.get('printX') ?? 0.3),
        yPct: Number(formData.get('printY') ?? 0.25),
        wPct: Number(formData.get('printW') ?? 0.4),
        hPct: Number(formData.get('printH') ?? 0.35),
      }
    : DEFAULT_PRINT_AREAS[mockup] ?? DEFAULT_PRINT_AREAS.tshirt;

  const hasCustomPrintAreaBack = String(formData.get('hasCustomPrintAreaBack') ?? '') === '1' && imageUrlBack;
  const printAreaBack = hasCustomPrintAreaBack
    ? {
        xPct: Number(formData.get('printXBack') ?? 0.3),
        yPct: Number(formData.get('printYBack') ?? 0.25),
        wPct: Number(formData.get('printWBack') ?? 0.4),
        hPct: Number(formData.get('printHBack') ?? 0.35),
      }
    : null;

  await prisma.product.create({
    data: {
      slug,
      name,
      category,
      description: description || `${name} — personalize it your way.`,
      basePrice: Math.round(priceDollars * 100),
      personalisedPrice: personalisedPriceDollars ? Math.round(Number(personalisedPriceDollars) * 100) : null,
      ...saleFields,
      colors: JSON.stringify(colors),
      colorPhotos: Object.keys(colorPhotos).length > 0 ? JSON.stringify(colorPhotos) : null,
      colorPhotosBack: Object.keys(colorPhotosBack).length > 0 ? JSON.stringify(colorPhotosBack) : null,
      sizes: sizes.length > 0 ? JSON.stringify(sizes) : null,
      mockup,
      printArea: JSON.stringify(printArea),
      printAreaBack: printAreaBack ? JSON.stringify(printAreaBack) : null,
      imageUrl,
      imageUrlBack,
      photoRecolorable,
      showOnPersonalised,
      showOnProducts,
      collections: { connect: collectionIds.map((id) => ({ id })) },
    },
  });

  redirect('/admin/products');
}

export async function updateProduct(formData: FormData) {
  const id = String(formData.get('id') ?? '');
  const name = String(formData.get('name') ?? '').trim();
  const category = String(formData.get('category') ?? '');
  const description = String(formData.get('description') ?? '').trim();
  const priceDollars = Number(formData.get('price') ?? 0);
  const personalisedPriceDollars = String(formData.get('personalisedPrice') ?? '').trim();
  const mockup = String(formData.get('mockup') ?? 'tshirt');
  const colorsInput = String(formData.get('colors') ?? '');
  const sizesInput = String(formData.get('sizes') ?? '');
  const imageFile = formData.get('image') as File | null;
  const imageBackFile = formData.get('imageBack') as File | null;
  const photoRecolorable = String(formData.get('photoRecolorable') ?? '') === '1';
  const showOnPersonalised = String(formData.get('showOnPersonalised') ?? '') === '1';
  const showOnProducts = String(formData.get('showOnProducts') ?? '') === '1';
  const collectionIds = formData.getAll('collections').map((v) => String(v));
  const referenceWidthCmInput = String(formData.get('referenceWidthCm') ?? '').trim();
  const referenceHeightCmInput = String(formData.get('referenceHeightCm') ?? '').trim();
  const referenceWidthCm = referenceWidthCmInput ? Number(referenceWidthCmInput) : null;
  const referenceHeightCm = referenceHeightCmInput ? Number(referenceHeightCmInput) : null;

  if (!id) throw new Error('Missing product id.');
  if (!name || !category || !priceDollars) {
    throw new Error('Name, category, and price are required.');
  }

  const colors = colorsInput
    .split(',')
    .map((c) => c.trim())
    .filter(Boolean);

  if (colors.length === 0) {
    throw new Error('At least one color is required.');
  }

  const sizes = sizesInput
    .split(',')
    .map((s) => s.trim())
    .filter(Boolean);

  const existing = await prisma.product.findUnique({ where: { id } });
  if (!existing) throw new Error('Product not found.');

  const existingColorPhotos: Record<string, string> = existing.colorPhotos
    ? JSON.parse(existing.colorPhotos)
    : {};
  const existingColorPhotosBack: Record<string, string> = existing.colorPhotosBack
    ? JSON.parse(existing.colorPhotosBack)
    : {};
  const newColorPhotos = await extractColorPhotos(formData, colors, 'front');
  const newColorPhotosBack = await extractColorPhotos(formData, colors, 'back');
  // Merge: newly uploaded photos override, everything else keeps whatever was
  // already saved (and only for colors still in the list).
  const mergedColorPhotos: Record<string, string> = {};
  const mergedColorPhotosBack: Record<string, string> = {};
  for (const hex of colors) {
    if (newColorPhotos[hex]) mergedColorPhotos[hex] = newColorPhotos[hex];
    else if (existingColorPhotos[hex]) mergedColorPhotos[hex] = existingColorPhotos[hex];

    if (newColorPhotosBack[hex]) mergedColorPhotosBack[hex] = newColorPhotosBack[hex];
    else if (existingColorPhotosBack[hex]) mergedColorPhotosBack[hex] = existingColorPhotosBack[hex];
  }

  const data: {
    name: string;
    category: string;
    description: string;
    basePrice: number;
    personalisedPrice: number | null;
    salePrice: number | null;
    saleStartsAt: Date | null;
    saleEndsAt: Date | null;
    colors: string;
    colorPhotos: string | null;
    colorPhotosBack: string | null;
    sizes: string | null;
    mockup: string;
    photoRecolorable: boolean;
    showOnPersonalised: boolean;
    showOnProducts: boolean;
    referenceWidthCm: number | null;
    referenceHeightCm: number | null;
    imageUrl?: string;
    imageUrlBack?: string;
    printArea?: string;
    printAreaBack?: string;
  } = {
    name,
    category,
    description: description || `${name} — personalize it your way.`,
    basePrice: Math.round(priceDollars * 100),
    personalisedPrice: personalisedPriceDollars ? Math.round(Number(personalisedPriceDollars) * 100) : null,
    ...parseSaleFields(formData),
    colors: JSON.stringify(colors),
    colorPhotos: Object.keys(mergedColorPhotos).length > 0 ? JSON.stringify(mergedColorPhotos) : null,
    colorPhotosBack: Object.keys(mergedColorPhotosBack).length > 0 ? JSON.stringify(mergedColorPhotosBack) : null,
    sizes: sizes.length > 0 ? JSON.stringify(sizes) : null,
    mockup,
    photoRecolorable,
    showOnPersonalised,
    showOnProducts,
    referenceWidthCm,
    referenceHeightCm,
  };

  // Only touch the front photo/print area if a new photo was actually uploaded —
  // otherwise leave whatever's already there alone.
  if (imageFile && imageFile.size > 0) {
    data.imageUrl = await fileToDataUrl(imageFile);

    const hasCustomPrintArea = String(formData.get('hasCustomPrintArea') ?? '') === '1';
    data.printArea = JSON.stringify(
      hasCustomPrintArea
        ? {
            xPct: Number(formData.get('printX') ?? 0.3),
            yPct: Number(formData.get('printY') ?? 0.25),
            wPct: Number(formData.get('printW') ?? 0.4),
            hPct: Number(formData.get('printH') ?? 0.35),
          }
        : DEFAULT_PRINT_AREAS[mockup] ?? DEFAULT_PRINT_AREAS.tshirt,
    );
  }

  if (imageBackFile && imageBackFile.size > 0) {
    data.imageUrlBack = await fileToDataUrl(imageBackFile);

    const hasCustomPrintAreaBack = String(formData.get('hasCustomPrintAreaBack') ?? '') === '1';
    if (hasCustomPrintAreaBack) {
      data.printAreaBack = JSON.stringify({
        xPct: Number(formData.get('printXBack') ?? 0.3),
        yPct: Number(formData.get('printYBack') ?? 0.25),
        wPct: Number(formData.get('printWBack') ?? 0.4),
        hPct: Number(formData.get('printHBack') ?? 0.35),
      });
    }
  }

  await prisma.product.update({
    where: { id },
    data: { ...data, collections: { set: collectionIds.map((cid) => ({ id: cid })) } },
  });

  redirect('/admin/products');
}

export async function deleteProduct(formData: FormData) {
  const id = String(formData.get('id') ?? '');
  if (!id) return;

  try {
    await prisma.product.delete({ where: { id } });
  } catch {
    // Product has existing orders/design proofs tied to it — deleting would
    // orphan that history, so we block it instead of cascading.
    redirect('/admin/products?error=in_use');
  }

  redirect('/admin/products');
}
