import { NextResponse } from "next/server";
import { createClient } from "@/lib/supabase-server";
import { canCompany, getCompanyContext } from "@/lib/company-context";
import { parseMoneyInput } from "@/utils/formatters";
import { validateAndNormalizePriceTiers } from "@/lib/price-tiers";
import { sanitizeProductDescription } from "@/lib/product-description-rich-text";
import { normalizeProductImagesInput } from "@/lib/product-images";

const writableFields = new Set([
  "name",
  "description",
  "price",
  "price_on_request",
  "direct_cost",
  "indirect_cost_rate",
  "stock",
  "unit_type",
  "category_id",
  "image_url",
  "show_in_catalog",
  "catalog_color",
  "active",
  "is_catalog_exclusive",
  "estimated_base_cost",
  "cost_total"
]);

type CompositionCostRow = { cost_total?: number | null };
type CompositionMaterialInput = {
  material_id?: string | null;
  consumption_qty?: number | null;
  waste_pct?: number | null;
  cost_total?: number | null;
};
type CompositionProcessInput = {
  kind?: "MACHINE" | "ROLE" | string | null;
  minutes?: number | null;
  setup_minutes?: number | null;
  cost_total?: number | null;
  machine_id?: string | null;
  role_id?: string | null;
  process_name?: string | null;
};
type ProductAttributeInput = {
  name?: string | null;
  options?: unknown;
  allow_multiple?: boolean | null;
  is_required?: boolean | null;
  position?: number | null;
};
type ProductVariationInput = {
  combination?: Record<string, string> | null;
  sku?: string | null;
  price?: number | null;
  stock?: number | null;
  cost_adjustment?: number | null;
};

function pickWritablePatch(value: unknown) {
  if (!value || typeof value !== "object" || Array.isArray(value)) return {};
  const record = value as Record<string, unknown>;
  const patch: Record<string, unknown> = {};
  for (const [key, v] of Object.entries(record)) {
    if (writableFields.has(key)) patch[key] = v;
  }
  return patch;
}

function requiresCatalogWrite(patch: Record<string, unknown>) {
  return Object.prototype.hasOwnProperty.call(patch, "show_in_catalog") || Object.prototype.hasOwnProperty.call(patch, "catalog_color");
}

function sanitizeAttributeOptionsWithImages(options: unknown, validImageIds: Set<string>) {
  if (!Array.isArray(options)) return options;
  return options.map((option) => {
    if (typeof option === "string") return option;
    if (!option || typeof option !== "object") return option;
    const row = option as Record<string, unknown>;
    const rawImageId = typeof row.image_id === "string" ? row.image_id : "";
    return {
      ...row,
      image_id: rawImageId && validImageIds.has(rawImageId) ? rawImageId : null,
    };
  });
}

function sanitizeAttributesWithImages(
  attributes: ProductAttributeInput[],
  validImageIds: Set<string>
): ProductAttributeInput[] {
  return attributes.map((attribute) => ({
    ...attribute,
    options: sanitizeAttributeOptionsWithImages(attribute.options, validImageIds),
  }));
}

async function getAuth() {
  const supabase = await createClient();
  const {
    data: { user },
  } = await supabase.auth.getUser();
  return { supabase, user };
}

export async function POST(req: Request) {
  const { supabase, user } = await getAuth();
  if (!user) return NextResponse.json({ error: "Não autenticado." }, { status: 401 });

  const ctx = await getCompanyContext(supabase, user);
  if (!canCompany(ctx, user.id, "products.write")) {
    return NextResponse.json({ error: "Sem permissão." }, { status: 403 });
  }

  const body = (await req.json().catch(() => null)) as Record<string, unknown> | null;
  const patch = pickWritablePatch(body);
  if (Object.prototype.hasOwnProperty.call(patch, "description")) {
    patch.description = sanitizeProductDescription(patch.description);
  }
  const { tiers: parsedPriceTiers, error: priceTierError } = validateAndNormalizePriceTiers(body?.price_tiers);
  if (priceTierError) {
    return NextResponse.json({ error: priceTierError }, { status: 400 });
  }

  const name = typeof patch.name === "string" ? patch.name.trim() : "";
  if (!name) return NextResponse.json({ error: "Nome é obrigatório." }, { status: 400 });

  const { images: normalizedImages, error: imageError } = normalizeProductImagesInput(body?.images);
  if (imageError) {
    return NextResponse.json({ error: imageError }, { status: 400 });
  }
  const validImageIds = new Set(normalizedImages.map((image) => image.id));

  const writeUserId = ctx.ownerId ?? ctx.companyId;
  const priceOnRequest = Boolean(patch.price_on_request);
  const normalizedPriceTiers = priceOnRequest ? [] : parsedPriceTiers;

  // Parse costs and price
  const estimatedBaseCost = typeof patch.estimated_base_cost === "number" ? patch.estimated_base_cost : parseMoneyInput(patch.estimated_base_cost);
  
  // Determine cost_total
  let finalCostTotal = estimatedBaseCost;
  const materials = Array.isArray(body?.materials)
    ? (body?.materials as CompositionMaterialInput[])
    : [];
  const processes = Array.isArray(body?.processes)
    ? (body?.processes as CompositionProcessInput[])
    : [];
  const attributes = sanitizeAttributesWithImages(
    Array.isArray(body?.attributes) ? (body?.attributes as ProductAttributeInput[]) : [],
    validImageIds
  );
  const variations = Array.isArray(body?.variations) ? body?.variations : [];

  const hasComposition = (Array.isArray(materials) && materials.length > 0) || (Array.isArray(processes) && processes.length > 0);

  if (hasComposition) {
      // If composition is provided, prefer the calculated cost from frontend or recalculate
      if (typeof patch.cost_total === 'number') {
          finalCostTotal = patch.cost_total;
      } else {
          const matCost = materials.reduce((acc, m) => acc + (Number(m.cost_total) || 0), 0);
          const procCost = processes.reduce((acc, p) => acc + (Number(p.cost_total) || 0), 0);
          finalCostTotal = matCost + procCost;
      }
  }

  let payload: Record<string, unknown> = { 
    ...patch, 
    user_id: writeUserId,
    estimated_base_cost: estimatedBaseCost,
    cost_total: finalCostTotal,
    direct_cost: estimatedBaseCost // Legacy support? Or keep sync? Let's assume direct_cost is deprecated or alias.
  };

  const primaryImageUrl =
    normalizedImages.find((image) => image.is_primary)?.image_url ||
    normalizedImages[0]?.image_url ||
    (typeof patch.image_url === "string" ? patch.image_url : "");
  payload = {
    ...payload,
    image_url: primaryImageUrl || null,
  };

  if (!priceOnRequest) {
    const manualPrice = typeof patch.price === "number" ? patch.price : parseMoneyInput(patch.price);
    payload = { ...payload, price: manualPrice };
  } else {
    payload = { ...payload, price: 0 };
  }

  const { data, error } = await supabase.from("products").insert(payload).select("id").single();
  if (error) return NextResponse.json({ error: error.message }, { status: 400 });

  if (data?.id) {
    const productId = data.id;
    const errors: string[] = [];

    if (normalizedPriceTiers.length > 0) {
      const tiersPayload = normalizedPriceTiers.map((tier) => ({
        product_id: productId,
        user_id: writeUserId,
        min_qty: tier.min_qty,
        max_qty: tier.max_qty,
        unit_price: tier.unit_price,
        sort_order: tier.sort_order ?? 0,
        active: tier.active ?? true,
      }));
      const { error: tiersError } = await supabase.from("product_price_tiers").insert(tiersPayload);
      if (tiersError) errors.push(`Price tiers: ${tiersError.message}`);
    }

    if (normalizedImages.length > 0) {
      const imagesPayload = normalizedImages.map((image) => ({
        id: image.id,
        product_id: productId,
        user_id: writeUserId,
        image_url: image.image_url,
        sort_order: image.sort_order,
        is_primary: image.is_primary,
      }));
      const { error: imagesError } = await supabase.from("product_images").insert(imagesPayload);
      if (imagesError) errors.push(`Images: ${imagesError.message}`);
    }

    if (hasComposition) {
      // Insert materials
      if (Array.isArray(materials) && materials.length > 0) {
        const materialsPayload = materials.map((m) => ({
          product_id: productId,
          user_id: writeUserId,
          material_id: m.material_id,
          consumption_qty: m.consumption_qty,
          waste_pct: m.waste_pct,
          cost_total: m.cost_total
        }));
        const { error: matError } = await supabase.from("product_materials").insert(materialsPayload);
        if (matError) errors.push(`Materials: ${matError.message}`);
      }

      // Insert processes
      if (Array.isArray(processes) && processes.length > 0) {
        const processesPayload = processes.map((p) => ({
          product_id: productId,
          user_id: writeUserId,
          kind: p.kind,
          minutes: p.minutes,
          setup_minutes: p.setup_minutes,
          cost_total: p.cost_total,
          machine_id: p.machine_id || null,
          role_id: p.role_id || null,
          process_name: p.process_name || "Processo"
        }));
        const { error: procError } = await supabase.from("product_processes").insert(processesPayload);
        if (procError) errors.push(`Processes: ${procError.message}`);
      }
    }

    // Insert attributes
    if (attributes.length > 0) {
      const attrsPayloadBase = attributes.map((a: ProductAttributeInput) => ({
        product_id: productId,
        name: a.name,
        options: a.options,
        position: typeof a.position === "number" ? a.position : null,
      }));
      const attrsPayload = attributes.map((a: ProductAttributeInput, index: number) => ({
        ...attrsPayloadBase[index],
        allow_multiple: a.allow_multiple ?? false,
        is_required: a.is_required ?? false,
      }));
      const { error: attrError } = await supabase.from("product_attributes").insert(attrsPayload);
      if (attrError) {
        if (
          attrError.message.includes("allow_multiple") ||
          attrError.message.includes("is_required") ||
          attrError.message.includes("position")
        ) {
          const fallbackPayload = attrsPayloadBase.map((item) => {
            const { position: droppedPosition, ...rest } = item;
            void droppedPosition;
            return rest;
          });
          const { error: fallbackError } = await supabase.from("product_attributes").insert(fallbackPayload);
          if (fallbackError) errors.push(`Attributes: ${fallbackError.message}`);
        } else {
          errors.push(`Attributes: ${attrError.message}`);
        }
      }
    }

    // Insert variations
    if (variations.length > 0) {
      const varsPayload = variations.map((v: ProductVariationInput) => ({
        product_id: productId,
        combination: v.combination,
        sku: v.sku,
        price: v.price,
        stock: v.stock,
        cost_adjustment: v.cost_adjustment
      }));
      const { error: varError } = await supabase.from("product_variations").insert(varsPayload);
      if (varError) errors.push(`Variations: ${varError.message}`);
    }

    if (errors.length > 0) {
      console.error("Errors inserting product data:", errors);
      // We don't fail the request completely, but log it.
      // Ideally we should transaction this or rollback, but Supabase HTTP API doesn't support transactions easily without RPC.
    }
  }

  return NextResponse.json({ ok: true, id: (data as { id?: unknown } | null)?.id ?? null });
}

export async function PATCH(req: Request) {
  const { supabase, user } = await getAuth();
  if (!user) return NextResponse.json({ error: "Não autenticado." }, { status: 401 });

  const body = (await req.json().catch(() => null)) as { id?: unknown; patch?: unknown } | null;
  const id = typeof body?.id === "string" ? body.id : "";
  if (!id) return NextResponse.json({ error: "Campo 'id' é obrigatório." }, { status: 400 });

  const patch = pickWritablePatch(body?.patch);
  if (Object.prototype.hasOwnProperty.call(patch, "description")) {
    patch.description = sanitizeProductDescription(patch.description);
  }
  const rawPatch = body?.patch as Record<string, unknown> | null;
  const hasImages = Boolean(rawPatch) && Object.prototype.hasOwnProperty.call(rawPatch, "images");
  const { images: normalizedImages, error: imageError } = normalizeProductImagesInput(
    hasImages ? rawPatch?.images : undefined
  );
  if (imageError) {
    return NextResponse.json({ error: imageError }, { status: 400 });
  }
  const validImageIds = new Set(normalizedImages.map((image) => image.id));
  const attributes = Array.isArray(rawPatch?.attributes)
    ? hasImages
      ? sanitizeAttributesWithImages(rawPatch.attributes as ProductAttributeInput[], validImageIds)
      : (rawPatch.attributes as ProductAttributeInput[])
    : null;
  const variations = Array.isArray(rawPatch?.variations) ? rawPatch?.variations : null;
  const hasPriceTiers = Boolean(rawPatch) && Object.prototype.hasOwnProperty.call(rawPatch, "price_tiers");
  const { tiers: parsedPriceTiers, error: priceTierError } = validateAndNormalizePriceTiers(
    hasPriceTiers ? rawPatch?.price_tiers : undefined
  );
  if (priceTierError) {
    return NextResponse.json({ error: priceTierError }, { status: 400 });
  }

  if (!Object.keys(patch).length && !attributes && !variations && !hasPriceTiers && !hasImages) {
    return NextResponse.json({ error: "Nada para atualizar." }, { status: 400 });
  }

  const ctx = await getCompanyContext(supabase, user);
  const permission = requiresCatalogWrite(patch) ? "catalog.write" : "products.write";
  if (!canCompany(ctx, user.id, permission)) {
    return NextResponse.json({ error: "Sem permissão." }, { status: 403 });
  }

  const tenantIds = ctx.tenantIds?.length ? ctx.tenantIds : [user.id];
  const { data: existing, error: existingError } = await supabase
    .from("products")
    .select("id, user_id, price, price_on_request, estimated_base_cost, cost_total, product_processes(cost_total), product_materials(cost_total)")
    .eq("id", id)
    .in("user_id", tenantIds)
    .maybeSingle();

  if (existingError) return NextResponse.json({ error: existingError.message }, { status: 400 });
  if (!existing) return NextResponse.json({ error: "Produto não encontrado." }, { status: 404 });

  // Recalculate cost_total if estimated_base_cost changed
  let costUpdate = {};
  if (Object.prototype.hasOwnProperty.call(patch, "estimated_base_cost")) {
    const newEstimated = typeof patch.estimated_base_cost === "number" ? patch.estimated_base_cost : parseMoneyInput(patch.estimated_base_cost);
    
    // Sum processes and materials cost
    const processesCost = ((existing.product_processes as CompositionCostRow[] | null) || []).reduce(
      (acc, p) => acc + (Number(p.cost_total) || 0),
      0
    );
    const materialsCost = ((existing.product_materials as CompositionCostRow[] | null) || []).reduce(
      (acc, m) => acc + (Number(m.cost_total) || 0),
      0
    );
    
    const newTotal = newEstimated + processesCost + materialsCost;
    
    costUpdate = {
      estimated_base_cost: newEstimated,
      cost_total: newTotal,
      direct_cost: newEstimated // Sync legacy field
    };
  }

  const hasPrice = Object.prototype.hasOwnProperty.call(patch, "price");
  const hasPriceOnRequest = Object.prototype.hasOwnProperty.call(patch, "price_on_request");
  const priceOnRequest = hasPriceOnRequest
    ? Boolean(patch.price_on_request)
    : Boolean((existing as { price_on_request?: unknown } | null)?.price_on_request);
  const normalizedPriceTiers = priceOnRequest ? [] : parsedPriceTiers;

  let patchWithPricing: Record<string, unknown> = { ...patch, ...costUpdate };
  if (hasPriceOnRequest || hasPrice) {
    if (priceOnRequest) {
      patchWithPricing = { ...patchWithPricing, price: 0 };
    } else if (hasPrice) {
      const manualPrice = typeof patch.price === "number" ? patch.price : parseMoneyInput(patch.price);
      patchWithPricing = { ...patchWithPricing, price: manualPrice };
    }
  }
  if (hasImages) {
    const primaryImageUrl =
      normalizedImages.find((image) => image.is_primary)?.image_url ||
      normalizedImages[0]?.image_url ||
      null;
    patchWithPricing = { ...patchWithPricing, image_url: primaryImageUrl };
  }

  const { data, error } = await supabase
    .from("products")
    .update(patchWithPricing)
    .eq("id", id)
    .in("user_id", tenantIds)
    .select("id")
    .maybeSingle();

  if (error) return NextResponse.json({ error: error.message }, { status: 400 });
  if (!data) return NextResponse.json({ error: "Produto não encontrado." }, { status: 404 });

  if (hasImages) {
    const { error: deleteImagesError } = await supabase.from("product_images").delete().eq("product_id", id);
    if (deleteImagesError) return NextResponse.json({ error: deleteImagesError.message }, { status: 400 });

    if (normalizedImages.length > 0) {
      const writeUserId = (existing as { user_id?: unknown } | null)?.user_id;
      const imagesPayload = normalizedImages.map((image) => ({
        id: image.id,
        product_id: id,
        user_id: typeof writeUserId === "string" ? writeUserId : (ctx.ownerId ?? ctx.companyId),
        image_url: image.image_url,
        sort_order: image.sort_order,
        is_primary: image.is_primary,
      }));
      const { error: insertImagesError } = await supabase.from("product_images").insert(imagesPayload);
      if (insertImagesError) return NextResponse.json({ error: insertImagesError.message }, { status: 400 });
    }
  }

  // Update Attributes & Variations
  if (attributes && variations) {
    // Delete existing
    await supabase.from("product_attributes").delete().eq("product_id", id);
    await supabase.from("product_variations").delete().eq("product_id", id);

    // Insert attributes
    if (attributes.length > 0) {
       const attrsPayloadBase = attributes.map((a: ProductAttributeInput) => ({
         product_id: id,
         name: a.name,
         options: a.options,
         position: typeof a.position === "number" ? a.position : null,
       }));
       const attrsPayload = attributes.map((a: ProductAttributeInput, index: number) => ({
         ...attrsPayloadBase[index],
         allow_multiple: a.allow_multiple ?? false,
         is_required: a.is_required ?? false,
       }));
       const { error: attrError } = await supabase.from("product_attributes").insert(attrsPayload);
       if (
         attrError &&
         (attrError.message.includes("allow_multiple") ||
           attrError.message.includes("is_required") ||
           attrError.message.includes("position"))
       ) {
         const fallbackPayload = attrsPayloadBase.map((item) => {
           const { position: droppedPosition, ...rest } = item;
           void droppedPosition;
           return rest;
         });
         await supabase.from("product_attributes").insert(fallbackPayload);
       }
    }

    // Insert variations
    if (variations.length > 0) {
       const varsPayload = variations.map((v: ProductVariationInput) => ({
         product_id: id,
         combination: v.combination,
         sku: v.sku,
         price: v.price,
         stock: v.stock,
         cost_adjustment: v.cost_adjustment
       }));
       await supabase.from("product_variations").insert(varsPayload);
    }
  }

  if (hasPriceTiers) {
    const { error: deleteTierError } = await supabase.from("product_price_tiers").delete().eq("product_id", id);
    if (deleteTierError) return NextResponse.json({ error: deleteTierError.message }, { status: 400 });

    if (normalizedPriceTiers.length > 0) {
      const writeUserId = (existing as { user_id?: unknown } | null)?.user_id;
      const tiersPayload = normalizedPriceTiers.map((tier) => ({
        product_id: id,
        user_id: typeof writeUserId === "string" ? writeUserId : (ctx.ownerId ?? ctx.companyId),
        min_qty: tier.min_qty,
        max_qty: tier.max_qty,
        unit_price: tier.unit_price,
        sort_order: tier.sort_order ?? 0,
        active: tier.active ?? true,
      }));
      const { error: insertTierError } = await supabase.from("product_price_tiers").insert(tiersPayload);
      if (insertTierError) return NextResponse.json({ error: insertTierError.message }, { status: 400 });
    }
  }

  return NextResponse.json({ ok: true });
}

export async function DELETE(req: Request) {
  const { supabase, user } = await getAuth();
  if (!user) return NextResponse.json({ error: "Não autenticado." }, { status: 401 });

  const body = (await req.json().catch(() => null)) as { id?: unknown } | null;
  const id = typeof body?.id === "string" ? body.id : "";
  if (!id) return NextResponse.json({ error: "Campo 'id' é obrigatório." }, { status: 400 });

  const ctx = await getCompanyContext(supabase, user);
  if (!canCompany(ctx, user.id, "products.write")) {
    return NextResponse.json({ error: "Sem permissão." }, { status: 403 });
  }

  const tenantIds = ctx.tenantIds?.length ? ctx.tenantIds : [user.id];
  const { data, error } = await supabase
    .from("products")
    .delete()
    .eq("id", id)
    .in("user_id", tenantIds)
    .select("id")
    .maybeSingle();

  if (error) return NextResponse.json({ error: error.message }, { status: 400 });
  if (!data) return NextResponse.json({ error: "Produto não encontrado." }, { status: 404 });

  return NextResponse.json({ ok: true });
}

