import { NextResponse } from "next/server";
import { createClient, createAdminClient } from "@/lib/supabase-server";
import { canCompany, getCompanyContext } from "@/lib/company-context";
import { avisarLoja } from "@/lib/loja/avisar-loja";

export const runtime = "nodejs";
export const dynamic = "force-dynamic";
export const revalidate = 0;

type ReviewStatus = "pending" | "approved" | "rejected";

type ReviewRow = {
  id: string;
  catalog_user_id: string;
  product_id: string;
  client_id: string;
  rating: number;
  comment: string | null;
  status: ReviewStatus;
  created_at: string;
  updated_at?: string | null;
  moderated_at?: string | null;
};

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

/* URL assinada por caminho, lembrada por 20 min: esta rota e consultada em
   polling e gerava uma assinatura no Storage por cliente a cada chamada. A
   assinatura vale 30 dias, entao 20 min de memoria nao arrisca nada. */
const ASSINADA_MS = 20 * 60 * 1000;
const assinadas = new Map<string, { url: string; ate: number }>();

async function resolveStorageUrl(admin: ReturnType<typeof createAdminClient>, rawValue: string | null | undefined) {
  const value = String(rawValue || "").trim();
  if (!value) return null;
  if (value.startsWith("data:")) return value;
  const lembrada = assinadas.get(value);
  if (lembrada && lembrada.ate > Date.now()) return lembrada.url;

  const parseStorageTarget = (input: string): { bucket: string; path: string } | null => {
    const normalized = input.replace(/^\/+/, "");

    if (/^https?:\/\//i.test(input)) {
      try {
        const parsed = new URL(input);
        const publicMarker = "/storage/v1/object/public/";
        const signMarker = "/storage/v1/object/sign/";
        const marker = parsed.pathname.includes(signMarker) ? signMarker : publicMarker;
        if (!parsed.pathname.includes(marker)) return null;
        const rest = parsed.pathname.slice(parsed.pathname.indexOf(marker) + marker.length);
        const [bucket, ...parts] = rest.split("/");
        return bucket && parts.length ? { bucket, path: parts.join("/") } : null;
      } catch {
        return null;
      }
    }

    const clean = normalized.replace(/^storage\/v1\/object\/public\//, "").replace(/^storage\/v1\/object\/sign\//, "");
    const [bucket, ...parts] = clean.split("/");
    return bucket && parts.length ? { bucket, path: parts.join("/") } : null;
  };

  const target = parseStorageTarget(value);
  if (!target) return value;

  const { data: signedData, error: signedError } = await admin.storage
    .from(target.bucket)
    .createSignedUrl(target.path, 60 * 60 * 24 * 30);

  if (!signedError && signedData?.signedUrl) {
    assinadas.set(value, { url: signedData.signedUrl, ate: Date.now() + ASSINADA_MS });
    return signedData.signedUrl;
  }

  const { data: publicData } = admin.storage.from(target.bucket).getPublicUrl(target.path);
  return publicData?.publicUrl || value;
}

export async function GET(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, "catalog.read")) {
    return NextResponse.json({ error: "Sem permissão." }, { status: 403 });
  }

  try {
    const admin = createAdminClient();
    const tenantIds = ctx.tenantIds?.length ? ctx.tenantIds : [ctx.ownerId ?? user.id];
    const { searchParams } = new URL(req.url);
    const reviewId = String(searchParams.get("reviewId") || "").trim();

    const reviewQuery = admin
      .from("product_reviews")
      .select("id,catalog_user_id,product_id,client_id,rating,comment,status,created_at,updated_at,moderated_at")
      .in("catalog_user_id", tenantIds);

    /* Sem limite a lista trazia toda avaliacao ja feita, em polling. As
       notificacoes usam as 10 pendentes mais novas; 100 sobra. */
    const { data: reviewRows, error: reviewError } = reviewId
      ? await reviewQuery.eq("id", reviewId).limit(1)
      : await reviewQuery.order("created_at", { ascending: false }).limit(100);

    if (reviewError) {
      return NextResponse.json({ error: "Falha ao carregar avaliações." }, { status: 500 });
    }

    const safeReviews = (reviewRows || []) as ReviewRow[];
    const productIds = Array.from(new Set(safeReviews.map((review) => review.product_id).filter(Boolean)));
    const clientIds = Array.from(new Set(safeReviews.map((review) => review.client_id).filter(Boolean)));

    const [{ data: productsData }, { data: clientsData }] = await Promise.all([
      productIds.length
        ? admin.from("products").select("id,name,image_url").in("id", productIds)
        : Promise.resolve({ data: [] } as any),
      clientIds.length
        ? admin.from("clients").select("id,name,avatar_url").in("id", clientIds)
        : Promise.resolve({ data: [] } as any),
    ]);

    const productsById = new Map<string, { name: string; image_url: string | null }>();
    ((productsData || []) as Array<{ id: string; name?: string | null; image_url?: string | null }>).forEach((product) => {
      productsById.set(product.id, {
        name: String(product.name || "Produto").trim() || "Produto",
        image_url: product.image_url || null,
      });
    });

    const resolvedClients = await Promise.all(
      ((clientsData || []) as Array<{ id: string; name?: string | null; avatar_url?: string | null }>).map(async (client) => ({
        id: client.id,
        name: String(client.name || "Cliente").trim() || "Cliente",
        avatar_url: await resolveStorageUrl(admin, client.avatar_url),
      }))
    );

    const clientsById = new Map<string, { name: string; avatar_url: string | null }>();
    resolvedClients.forEach((client) => {
      clientsById.set(client.id, {
        name: client.name,
        avatar_url: client.avatar_url,
      });
    });

    const mappedReviews = safeReviews.map((review) => ({
      ...review,
      product_name: productsById.get(review.product_id)?.name || "Produto",
      product_image_url: productsById.get(review.product_id)?.image_url || null,
      client_name: clientsById.get(review.client_id)?.name || "Cliente",
      client_avatar_url: clientsById.get(review.client_id)?.avatar_url || null,
    }));

    if (reviewId) {
      return NextResponse.json({
        ok: true,
        review: mappedReviews[0] || null,
      });
    }

    return NextResponse.json({
      ok: true,
      reviews: mappedReviews,
    });
  } catch {
    return NextResponse.json({ error: "Falha ao carregar avaliações." }, { status: 500 });
  }
}

export async function PATCH(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, "catalog.reviews.write")) {
    return NextResponse.json({ error: "Sem permissão." }, { status: 403 });
  }

  const body = (await req.json().catch(() => null)) as Record<string, unknown> | null;
  const reviewId = typeof body?.reviewId === "string" ? body.reviewId.trim() : "";
  const status = body?.status === "approved" || body?.status === "rejected" || body?.status === "pending"
    ? (body.status as ReviewStatus)
    : null;
  const comment = typeof body?.comment === "string" ? body.comment.trim() : "";

  if (!reviewId) {
    return NextResponse.json({ error: "Avaliação inválida." }, { status: 400 });
  }

  if (!comment || comment.length < 3) {
    return NextResponse.json({ error: "Informe um comentário válido." }, { status: 400 });
  }

  try {
    const admin = createAdminClient();
    const tenantIds = ctx.tenantIds?.length ? ctx.tenantIds : [ctx.ownerId ?? user.id];

    const { data: existingReview, error: existingError } = await admin
      .from("product_reviews")
      .select("id,catalog_user_id")
      .eq("id", reviewId)
      .in("catalog_user_id", tenantIds)
      .maybeSingle();

    if (existingError || !existingReview?.id) {
      return NextResponse.json({ error: "Avaliação não encontrada." }, { status: 404 });
    }

    const timestamp = new Date().toISOString();
    const payload: Record<string, unknown> = {
      comment,
      moderated_at: timestamp,
    };

    if (status) {
      payload.status = status;
      payload.approved_at = status === "approved" ? timestamp : null;
      payload.rejected_at = status === "rejected" ? timestamp : null;
    }

    const { error: updateError } = await admin
      .from("product_reviews")
      .update(payload)
      .eq("id", reviewId);

    if (updateError) {
      return NextResponse.json({ error: "Não foi possível atualizar a avaliação." }, { status: 500 });
    }

    /* A página do produto guarda as avaliações aprovadas por dois minutos. */
    avisarLoja(String(existingReview.catalog_user_id || "") || (ctx.ownerId ?? user.id), req);
    return NextResponse.json({ ok: true });
  } catch {
    return NextResponse.json({ error: "Não foi possível atualizar a avaliação." }, { status: 500 });
  }
}
