import { http, HttpResponse } from "msw";
import { z } from "zod";
import { RELANCE_TONES } from "@rubis/shared";

import { mockDb } from "../db";
import { userIdFromAuthHeader } from "./auth";

const apiBase = "*/api/v1";

function unauthenticated() {
  return HttpResponse.json(
    { errors: [{ code: "unauthenticated", message: "Non authentifié" }] },
    { status: 401 },
  );
}

function notFound() {
  return HttpResponse.json(
    { errors: [{ code: "not_found", message: "Plan introuvable" }] },
    { status: 404 },
  );
}

function authedOrgId(authHeader: string | null): string | undefined {
  const userId = userIdFromAuthHeader(authHeader);
  if (!userId) return undefined;
  return mockDb.findUserById(userId)?.organizationId;
}

const updatePlanStepSchema = z.object({
  id: z.string().optional(),
  order: z.number().int().min(0),
  offsetDays: z.number().int().min(-30).max(180),
  tone: z.enum(RELANCE_TONES),
  subject: z.string().min(1).max(200),
  body: z.string().min(1).max(5000),
  requiresManualValidation: z.boolean(),
});

const updatePlanSchema = z.object({
  name: z.string().min(1).max(80).optional(),
  description: z.string().max(500).optional(),
  steps: z.array(updatePlanStepSchema).min(1).max(10).optional(),
});

const createPlanSchema = z.object({
  name: z.string().min(1).max(80),
  description: z.string().max(500).optional(),
  steps: z.array(updatePlanStepSchema).min(1).max(10),
});

const RESERVED_SLUGS = new Set(["nouveau", "new", "create"]);

function slugify(input: string): string {
  return (
    input
      .normalize("NFD")
      .replace(/[̀-ͯ]/gu, "")
      .toLowerCase()
      .replace(/[^a-z0-9]+/gu, "-")
      .replace(/^-+|-+$/gu, "")
      .slice(0, 60) || "plan"
  );
}

function uniqueSlug(orgId: string, base: string): string {
  const start = RESERVED_SLUGS.has(base) ? `${base}-1` : base;
  const taken = new Set(mockDb.listPlansForOrg(orgId).map((p) => p.slug));
  if (!taken.has(start) && !RESERVED_SLUGS.has(start)) return start;
  for (let i = 2; i < 100; i++) {
    const candidate = `${base}-${i}`;
    if (!taken.has(candidate) && !RESERVED_SLUGS.has(candidate)) return candidate;
  }
  return `${base}-${Date.now()}`;
}

export const planHandlers = [
  // GET /api/v1/plans — liste enrichie avec compteurs d'usage
  http.get(`${apiBase}/plans`, ({ request }) => {
    const orgId = authedOrgId(request.headers.get("authorization"));
    if (!orgId) return unauthenticated();

    const plans = mockDb.listPlansForOrg(orgId);
    const invoices = mockDb.listInvoicesForOrg(orgId);

    const enriched = plans.map((plan) => {
      const usageCount = invoices.filter(
        (inv) => inv.planId === plan.id && inv.status !== "paid" && inv.status !== "cancelled",
      ).length;
      return { ...plan, usageCount };
    });

    return HttpResponse.json({ data: enriched });
  }),

  // GET /api/v1/plans/:slug — détail (par slug pour les plans pré-fournis)
  http.get(`${apiBase}/plans/:slug`, ({ request, params }) => {
    const orgId = authedOrgId(request.headers.get("authorization"));
    if (!orgId) return unauthenticated();

    const slug = params.slug as string;
    const plan = mockDb.findPlanBySlug(orgId, slug);
    if (!plan) return notFound();

    const invoices = mockDb.listInvoicesForOrg(orgId);
    const usageCount = invoices.filter(
      (inv) => inv.planId === plan.id && inv.status !== "paid" && inv.status !== "cancelled",
    ).length;

    return HttpResponse.json({ data: { ...plan, usageCount } });
  }),

  // POST /api/v1/plans — création d'un plan custom (slug auto-généré)
  http.post(`${apiBase}/plans`, async ({ request }) => {
    const orgId = authedOrgId(request.headers.get("authorization"));
    if (!orgId) return unauthenticated();

    const json = await request.json();
    const parsed = createPlanSchema.safeParse(json);
    if (!parsed.success) {
      return HttpResponse.json(
        {
          errors: parsed.error.issues.map((i) => ({
            code: "validation_failed",
            message: i.message,
            field: i.path.join("."),
          })),
        },
        { status: 422 },
      );
    }

    const slug = uniqueSlug(orgId, slugify(parsed.data.name));
    const planId = `plan_${crypto.randomUUID()}`;
    const created = mockDb.createPlan(orgId, {
      slug,
      name: parsed.data.name,
      description: parsed.data.description ?? "",
      isDefault: false,
      steps: parsed.data.steps.map((s, idx) => ({
        ...s,
        id: s.id ?? `step_${planId}_${idx}_${Date.now()}`,
      })),
    });
    return HttpResponse.json({ data: created }, { status: 201 });
  }),

  // PATCH /api/v1/plans/:slug — sauvegarde nom/description/étapes
  http.patch(`${apiBase}/plans/:slug`, async ({ request, params }) => {
    const orgId = authedOrgId(request.headers.get("authorization"));
    if (!orgId) return unauthenticated();

    const slug = params.slug as string;
    const plan = mockDb.findPlanBySlug(orgId, slug);
    if (!plan) return notFound();

    const json = await request.json();
    const parsed = updatePlanSchema.safeParse(json);
    if (!parsed.success) {
      return HttpResponse.json(
        {
          errors: parsed.error.issues.map((i) => ({
            code: "validation_failed",
            message: i.message,
            field: i.path.join("."),
          })),
        },
        { status: 422 },
      );
    }

    // Recompose les étapes en garantissant un id sur chaque
    const steps = parsed.data.steps?.map((s, idx) => ({
      ...s,
      id: s.id ?? `step_${plan.id}_${idx}_${Date.now()}`,
    }));

    const updated = mockDb.updatePlan(orgId, plan.id, {
      ...(parsed.data.name !== undefined && { name: parsed.data.name }),
      ...(parsed.data.description !== undefined && {
        description: parsed.data.description,
      }),
      ...(steps !== undefined && { steps }),
    });

    return HttpResponse.json({ data: updated });
  }),
];
