import { z } from "zod";
import type { Request } from "express";
import { COOKIE_NAME } from "@shared/const";
import type { User } from "../drizzle/schema";
import { createSessionToken, loginLocalUser } from "./_core/auth";
import * as db from "./db";

export const OPERATIONAL_TOUR_SETTINGS_KEY = "system.operationalTour.settings";
export const OPERATIONAL_TOUR_HISTORY_KEY = "system.operationalTour.history";

export const operationalTourEnvironmentSchema = z.enum(["production", "secondary"]);
export type OperationalTourEnvironment = z.infer<typeof operationalTourEnvironmentSchema>;
export const operationalTourAuthModeSchema = z.enum(["local", "oauth"]);
export type OperationalTourAuthMode = z.infer<typeof operationalTourAuthModeSchema>;
export const operationalTourStepKindSchema = z.enum(["technical", "user"]);
export type OperationalTourStepKind = z.infer<typeof operationalTourStepKindSchema>;

const operationalTourTargetSchema = z.object({
  baseUrl: z.string().trim().optional().default(""),
  associationEmail: z.string().trim().optional().default(""),
  associationPassword: z.string().trim().optional().default(""),
  associationAuthMode: operationalTourAuthModeSchema.optional().default("local"),
  accueilEmail: z.string().trim().optional().default(""),
  accueilPassword: z.string().trim().optional().default(""),
  accueilAuthMode: operationalTourAuthModeSchema.optional().default("local"),
  adminEmail: z.string().trim().optional().default(""),
  adminPassword: z.string().trim().optional().default(""),
  adminAuthMode: operationalTourAuthModeSchema.optional().default("local"),
});

const EMPTY_TARGET = {
  baseUrl: "",
  associationEmail: "",
  associationPassword: "",
  associationAuthMode: "local",
  accueilEmail: "",
  accueilPassword: "",
  accueilAuthMode: "local",
  adminEmail: "",
  adminPassword: "",
  adminAuthMode: "local",
} as const;

const operationalTourSettingsSchema = z.object({
  defaultEnvironment: operationalTourEnvironmentSchema.default("production"),
  targets: z.object({
    production: operationalTourTargetSchema.default(EMPTY_TARGET),
    secondary: operationalTourTargetSchema.default(EMPTY_TARGET),
  }),
});

export type OperationalTourSettings = z.infer<typeof operationalTourSettingsSchema>;

const operationalTourStepSchema = z.object({
  id: z.string(),
  label: z.string(),
  kind: operationalTourStepKindSchema.default("technical"),
  status: z.enum(["ok", "warning", "error"]),
  durationMs: z.number().int().nonnegative(),
  message: z.string(),
  details: z.string().optional(),
  url: z.string().optional(),
  critical: z.boolean().default(true),
});

export type OperationalTourStep = z.infer<typeof operationalTourStepSchema>;

const operationalTourReportSchema = z.object({
  id: z.string(),
  environment: operationalTourEnvironmentSchema,
  environmentLabel: z.string(),
  baseUrl: z.string(),
  status: z.enum(["ok", "warning", "error"]),
  startedAt: z.string(),
  finishedAt: z.string(),
  durationMs: z.number().int().nonnegative(),
  initiatedBy: z.object({
    userId: z.number().int().positive(),
    name: z.string(),
    email: z.string().optional(),
  }),
  summary: z.object({
    total: z.number().int().nonnegative(),
    ok: z.number().int().nonnegative(),
    warning: z.number().int().nonnegative(),
    error: z.number().int().nonnegative(),
  }),
  steps: z.array(operationalTourStepSchema),
});

export type OperationalTourReport = z.infer<typeof operationalTourReportSchema>;

const OPERATIONAL_TOUR_HISTORY_MAX_BYTES = 60_000;
const OPERATIONAL_TOUR_HISTORY_MAX_REPORTS = 12;
const OPERATIONAL_TOUR_DUPLICATE_WINDOW_MS = 15_000;

const EMPTY_SETTINGS: OperationalTourSettings = {
  defaultEnvironment: "production",
  targets: {
    production: {
      baseUrl: "https://www.portail-association973.com",
      associationEmail: "",
      associationPassword: "",
      associationAuthMode: "local",
      accueilEmail: "",
      accueilPassword: "",
      accueilAuthMode: "local",
      adminEmail: "",
      adminPassword: "",
      adminAuthMode: "local",
    },
    secondary: {
      baseUrl: "",
      associationEmail: "",
      associationPassword: "",
      associationAuthMode: "local",
      accueilEmail: "",
      accueilPassword: "",
      accueilAuthMode: "local",
      adminEmail: "",
      adminPassword: "",
      adminAuthMode: "local",
    },
  },
};

type PageCheckOptions = {
  id: string;
  label: string;
  kind: OperationalTourStepKind;
  url: string;
  cookieHeader?: string | null;
  markers?: string[];
  expectedPath?: string;
  critical?: boolean;
};

type PageCheckResult = OperationalTourStep;

function normalizeBaseUrl(value: string | undefined) {
  return String(value || "").trim().replace(/\/+$/, "");
}

function normalizeEmail(value: string | undefined) {
  return String(value || "").trim().toLowerCase();
}

function buildRequestOrigin(req: Request) {
  const forwardedProto = typeof req.headers["x-forwarded-proto"] === "string" ? req.headers["x-forwarded-proto"] : undefined;
  const forwardedHost = typeof req.headers["x-forwarded-host"] === "string" ? req.headers["x-forwarded-host"] : undefined;
  const protocol = forwardedProto || req.protocol || "http";
  const host = forwardedHost || req.get?.("host") || "";
  return host ? `${protocol}://${host}` : "";
}

function isLoginPage(body: string, finalUrl: string) {
  return finalUrl.includes("/login") || body.includes("Connexion requise") || body.includes("Se connecter");
}

function normalizePathname(url: string) {
  try {
    const pathname = new URL(url).pathname || "/";
    return pathname === "/" ? "/" : pathname.replace(/\/+$/, "");
  } catch {
    return url;
  }
}

function hasReactShell(body: string, contentType: string | null) {
  const lowerBody = body.toLowerCase();
  const lowerType = String(contentType || "").toLowerCase();
  return (
    lowerType.includes("text/html") &&
    (lowerBody.includes("<!doctype html") || lowerBody.includes("<html")) &&
    (lowerBody.includes("<title>") || lowerBody.includes("script type=\"module\"") || lowerBody.includes("id=\"manus-runtime\""))
  );
}

function buildStaticStep(input: Omit<OperationalTourStep, "durationMs"> & { durationMs?: number }): OperationalTourStep {
  return {
    ...input,
    durationMs: input.durationMs ?? 0,
  };
}

async function fetchPageCheck(options: PageCheckOptions): Promise<PageCheckResult> {
  const started = Date.now();
  try {
    const response = await fetch(options.url, {
      headers: options.cookieHeader ? { cookie: options.cookieHeader } : undefined,
      redirect: "follow",
    });
    const body = await response.text();
    const finalUrl = response.url || options.url;
    const contentType = response.headers.get("content-type");
    const expectedPath = normalizePathname(options.expectedPath || options.url);
    const finalPath = normalizePathname(finalUrl);
    const markers = options.markers || [];
    const matchedMarker = markers.find((marker) => body.includes(marker));
    const routeMatches = expectedPath === "/" ? finalPath === "/" : finalPath === expectedPath || finalPath.startsWith(`${expectedPath}/`);
    const reactShell = hasReactShell(body, contentType);

    if (!response.ok) {
      return {
        id: options.id,
        label: options.label,
        kind: options.kind,
        status: "error",
        durationMs: Date.now() - started,
        message: `HTTP ${response.status}`,
        details: finalUrl,
        url: options.url,
        critical: options.critical ?? true,
      };
    }

    if (options.cookieHeader && isLoginPage(body, finalUrl)) {
      return {
        id: options.id,
        label: options.label,
        kind: options.kind,
        status: "error",
        durationMs: Date.now() - started,
        message: "Authentification non reconnue",
        details: finalUrl,
        url: options.url,
        critical: options.critical ?? true,
      };
    }

    if (!routeMatches) {
      return {
        id: options.id,
        label: options.label,
        kind: options.kind,
        status: "warning",
        durationMs: Date.now() - started,
        message: "Page servie sur un chemin inattendu",
        details: `${finalUrl} (attendu: ${expectedPath})`,
        url: options.url,
        critical: options.critical ?? true,
      };
    }

    if (matchedMarker) {
      return {
        id: options.id,
        label: options.label,
        kind: options.kind,
        status: "ok",
        durationMs: Date.now() - started,
        message: `Contrôle réussi (${matchedMarker})`,
        details: finalUrl,
        url: options.url,
        critical: options.critical ?? true,
      };
    }

    if (reactShell) {
      return {
        id: options.id,
        label: options.label,
        kind: options.kind,
        status: "ok",
        durationMs: Date.now() - started,
        message: "Page accessible, rendu client servi",
        details: finalUrl,
        url: options.url,
        critical: options.critical ?? true,
      };
    }

    return {
      id: options.id,
      label: options.label,
      kind: options.kind,
      status: markers.length > 0 ? "warning" : "ok",
      durationMs: Date.now() - started,
      message: markers.length > 0 ? "Page accessible, marqueur visuel non confirmé" : "Contrôle réussi",
      details: finalUrl,
      url: options.url,
      critical: options.critical ?? true,
    };
  } catch (error) {
    return {
      id: options.id,
      label: options.label,
      kind: options.kind,
      status: "error",
      durationMs: Date.now() - started,
      message: error instanceof Error ? error.message : "Erreur réseau",
      url: options.url,
      critical: options.critical ?? true,
    };
  }
}

async function fetchJsonCheck(options: {
  id: string;
  label: string;
  kind: OperationalTourStepKind;
  url: string;
  cookieHeader?: string | null;
  markers?: string[];
  critical?: boolean;
}) {
  const started = Date.now();
  try {
    const response = await fetch(options.url, {
      headers: {
        accept: "application/json",
        ...(options.cookieHeader ? { cookie: options.cookieHeader } : {}),
      },
      redirect: "follow",
    });
    const body = await response.text();
    const contentType = String(response.headers.get("content-type") || "").toLowerCase();
    const matchesMarker = (options.markers || []).some((marker) => body.includes(marker));
    if (!response.ok) {
      return buildStaticStep({
        id: options.id,
        label: options.label,
        kind: options.kind,
        status: "error",
        message: `HTTP ${response.status}`,
        details: response.url || options.url,
        url: options.url,
        critical: options.critical ?? true,
        durationMs: Date.now() - started,
      });
    }
    if (!contentType.includes("application/json")) {
      return buildStaticStep({
        id: options.id,
        label: options.label,
        kind: options.kind,
        status: "warning",
        message: "Réponse non JSON",
        details: response.url || options.url,
        url: options.url,
        critical: options.critical ?? true,
        durationMs: Date.now() - started,
      });
    }
    return buildStaticStep({
      id: options.id,
      label: options.label,
      kind: options.kind,
      status: matchesMarker || !(options.markers || []).length ? "ok" : "warning",
      message: matchesMarker || !(options.markers || []).length ? "API accessible et réponse JSON valide" : "API accessible, signature JSON à confirmer",
      details: response.url || options.url,
      url: options.url,
      critical: options.critical ?? true,
      durationMs: Date.now() - started,
    });
  } catch (error) {
    return buildStaticStep({
      id: options.id,
      label: options.label,
      kind: options.kind,
      status: "error",
      message: error instanceof Error ? error.message : "Erreur réseau",
      url: options.url,
      critical: options.critical ?? true,
      durationMs: Date.now() - started,
    });
  }
}

async function fetchHomeDiagnostics(baseUrl: string) {
  const started = Date.now();
  try {
    const response = await fetch(`${baseUrl}/`, { redirect: "follow" });
    const body = await response.text();
    return {
      durationMs: Date.now() - started,
      body,
      headers: response.headers,
      ok: response.ok,
      finalUrl: response.url || `${baseUrl}/`,
    };
  } catch (error) {
    return {
      durationMs: Date.now() - started,
      body: "",
      headers: new Headers(),
      ok: false,
      finalUrl: `${baseUrl}/`,
      error: error instanceof Error ? error.message : "Erreur réseau",
    };
  }
}

async function createConfiguredSessionCookie(email: string, password?: string) {
  const normalizedEmail = normalizeEmail(email);
  const normalizedPassword = String(password || "");
  let user;

  if (normalizedPassword) {
    user = await loginLocalUser({ email: normalizedEmail, password: normalizedPassword });
  } else {
    const existingUser = await db.getUserByEmail(normalizedEmail);
    if (!existingUser) {
      throw new Error("Compte introuvable");
    }
    if (!existingUser.isActive) {
      throw new Error("Compte inactif");
    }
    if (!existingUser.loginMethod || !existingUser.loginMethod.endsWith("_oauth")) {
      throw new Error("Mot de passe requis pour ce compte");
    }
    user = existingUser;
  }

  const token = await createSessionToken(user);
  return `${COOKIE_NAME}=${token}`;
}

async function tryFindPublicProfileUrl(baseUrl: string) {
  try {
    const response = await fetch(`${baseUrl}/associations`, { redirect: "follow" });
    if (!response.ok) return null;
    const body = await response.text();
    const match = body.match(/href="(\/associations\/[^"]+)"/);
    if (match) return `${baseUrl}${match[1]}`;
  } catch {
    // fallback below
  }

  try {
    const directory = await db.listAssociationDirectoryMapEntries({ limit: 1 });
    const firstEntry = directory.data?.[0];
    return firstEntry?.id ? `${baseUrl}/associations/${firstEntry.id}` : null;
  } catch {
    return null;
  }
}

function computeReportStatus(steps: OperationalTourStep[]): OperationalTourReport["status"] {
  const hasCriticalError = steps.some((step) => step.critical !== false && step.status === "error");
  if (hasCriticalError) return "error";
  const hasWarning = steps.some((step) => step.status !== "ok");
  return hasWarning ? "warning" : "ok";
}

function summarizeSteps(steps: OperationalTourStep[]) {
  return {
    total: steps.length,
    ok: steps.filter((step) => step.status === "ok").length,
    warning: steps.filter((step) => step.status === "warning").length,
    error: steps.filter((step) => step.status === "error").length,
  };
}

function summarizeStepsByKind(steps: OperationalTourStep[], kind: OperationalTourStepKind) {
  return summarizeSteps(steps.filter((step) => step.kind === kind));
}

export function sanitizeOperationalTourSettings(input?: unknown): OperationalTourSettings {
  const parsed = operationalTourSettingsSchema.safeParse(input);
  if (!parsed.success) return EMPTY_SETTINGS;
  return {
    defaultEnvironment: parsed.data.defaultEnvironment,
    targets: {
      production: {
        ...EMPTY_SETTINGS.targets.production,
        ...parsed.data.targets.production,
        baseUrl: normalizeBaseUrl(parsed.data.targets.production.baseUrl) || EMPTY_SETTINGS.targets.production.baseUrl,
        associationAuthMode: parsed.data.targets.production.associationAuthMode || "local",
        accueilAuthMode: parsed.data.targets.production.accueilAuthMode || "local",
        adminAuthMode: parsed.data.targets.production.adminAuthMode || "local",
      },
      secondary: {
        ...EMPTY_SETTINGS.targets.secondary,
        ...parsed.data.targets.secondary,
        baseUrl: normalizeBaseUrl(parsed.data.targets.secondary.baseUrl),
        associationAuthMode: parsed.data.targets.secondary.associationAuthMode || "local",
        accueilAuthMode: parsed.data.targets.secondary.accueilAuthMode || "local",
        adminAuthMode: parsed.data.targets.secondary.adminAuthMode || "local",
      },
    },
  };
}

export async function getOperationalTourSettings() {
  const raw = await db.getPortalSetting(OPERATIONAL_TOUR_SETTINGS_KEY);
  if (!raw) return EMPTY_SETTINGS;
  try {
    return sanitizeOperationalTourSettings(JSON.parse(raw));
  } catch {
    return EMPTY_SETTINGS;
  }
}

export async function saveOperationalTourSettings(settings: OperationalTourSettings) {
  const sanitized = sanitizeOperationalTourSettings(settings);
  await db.setPortalSetting(
    OPERATIONAL_TOUR_SETTINGS_KEY,
    JSON.stringify(sanitized),
    "Configuration du tour opérationnel du site"
  );
  return sanitized;
}

export async function getOperationalTourHistory() {
  const raw = await db.getPortalSetting(OPERATIONAL_TOUR_HISTORY_KEY);
  if (!raw) return [] as OperationalTourReport[];
  try {
    const parsed = z.array(operationalTourReportSchema).parse(JSON.parse(raw));
    return parsed;
  } catch {
    return [] as OperationalTourReport[];
  }
}

function compactOperationalTourHistory(history: OperationalTourReport[]) {
  const compactStep = (step: OperationalTourStep): OperationalTourStep => ({
    id: step.id,
    label: step.label,
    kind: step.kind,
    status: step.status,
    durationMs: step.durationMs,
    message: step.message,
    critical: step.critical ?? true,
    ...(step.status !== "ok" && step.details ? { details: step.details } : {}),
    ...(step.status !== "ok" && step.url ? { url: step.url } : {}),
  });

  const buildSignature = (report: OperationalTourReport) => JSON.stringify({
    environment: report.environment,
    baseUrl: report.baseUrl,
    status: report.status,
    summary: report.summary,
    steps: report.steps.map((step) => ({
      id: step.id,
      kind: step.kind,
      status: step.status,
      message: step.message,
      details: step.details || "",
    })),
  });

  const dedupedHistory: OperationalTourReport[] = [];
  for (const report of history) {
    const previous = dedupedHistory[dedupedHistory.length - 1];
    if (previous && previous.environment === report.environment) {
      const previousTime = new Date(previous.finishedAt).getTime();
      const currentTime = new Date(report.finishedAt).getTime();
      if (
        Number.isFinite(previousTime) &&
        Number.isFinite(currentTime) &&
        Math.abs(currentTime - previousTime) <= OPERATIONAL_TOUR_DUPLICATE_WINDOW_MS &&
        buildSignature(previous) === buildSignature(report)
      ) {
        dedupedHistory[dedupedHistory.length - 1] = report;
        continue;
      }
    }
    dedupedHistory.push(report);
  }

  let reports = dedupedHistory.slice(0, OPERATIONAL_TOUR_HISTORY_MAX_REPORTS).map((report) => ({
    ...report,
    steps: report.steps.map(compactStep),
  }));

  while (reports.length > 0) {
    const serialized = JSON.stringify(reports);
    if (Buffer.byteLength(serialized, "utf8") <= OPERATIONAL_TOUR_HISTORY_MAX_BYTES) {
      return reports;
    }
    reports = reports.slice(0, -1);
  }

  return [];
}

export async function saveOperationalTourHistory(history: OperationalTourReport[]) {
  await db.setPortalSetting(
    OPERATIONAL_TOUR_HISTORY_KEY,
    JSON.stringify(compactOperationalTourHistory(history)),
    "Historique du tour opérationnel du site"
  );
}

export async function clearOperationalTourHistory() {
  const history = await getOperationalTourHistory();
  const seen = new Set<OperationalTourEnvironment>();
  const retained = history.filter((report) => {
    if (seen.has(report.environment)) {
      return false;
    }
    seen.add(report.environment);
    return true;
  });
  await saveOperationalTourHistory(retained);
  return retained;
}

export async function runOperationalTour(params: {
  environment: OperationalTourEnvironment;
  req: Request;
  currentUser: User;
}) {
  const startedAt = new Date();
  const settings = await getOperationalTourSettings();
  const target = settings.targets[params.environment];
  const baseUrl = normalizeBaseUrl(target.baseUrl);
  const environmentLabel = params.environment === "production" ? "Production" : "Préproduction / Local";
  const steps: OperationalTourStep[] = [];

  if (!baseUrl) {
    const report: OperationalTourReport = {
      id: `tour-${Date.now()}`,
      environment: params.environment,
      environmentLabel,
      baseUrl: "",
      status: "error",
      startedAt: startedAt.toISOString(),
      finishedAt: new Date().toISOString(),
      durationMs: 0,
      initiatedBy: {
        userId: params.currentUser.id,
        name: params.currentUser.name || params.currentUser.email || `Utilisateur #${params.currentUser.id}`,
        email: params.currentUser.email || undefined,
      },
      summary: { total: 1, ok: 0, warning: 0, error: 1 },
      steps: [
        {
          id: "configuration",
          label: "Configuration de l’environnement",
          kind: "technical",
          status: "error",
          durationMs: 0,
          message: "URL cible manquante",
          details: `Aucune URL n’est configurée pour ${environmentLabel.toLowerCase()}.`,
          critical: true,
        },
      ],
    };
    const nextHistory = [report, ...(await getOperationalTourHistory())];
    await saveOperationalTourHistory(nextHistory);
    return report;
  }

  const currentOrigin = buildRequestOrigin(params.req);
  const isSameOrigin = currentOrigin && normalizeBaseUrl(currentOrigin) === baseUrl;
  const incomingCookieHeader = typeof params.req.headers.cookie === "string" ? params.req.headers.cookie : "";

  const homeDiagnostics = await fetchHomeDiagnostics(baseUrl);

  if (homeDiagnostics.ok) {
    steps.push(
      buildStaticStep({
        id: "technical-performance-home",
        label: "Performance de l’accueil",
        kind: "technical",
        status: homeDiagnostics.durationMs <= 1500 ? "ok" : homeDiagnostics.durationMs <= 3000 ? "warning" : "error",
        message:
          homeDiagnostics.durationMs <= 1500
            ? "Temps de réponse confortable"
            : homeDiagnostics.durationMs <= 3000
              ? "Temps de réponse correct mais à surveiller"
              : "Temps de réponse trop élevé",
        details: `${homeDiagnostics.durationMs} ms`,
        critical: false,
        durationMs: homeDiagnostics.durationMs,
      }),
      buildStaticStep({
        id: "technical-security-baseline",
        label: "Sécurité de base",
        kind: "technical",
        status: baseUrl.startsWith("https://") ? "ok" : "warning",
        message: baseUrl.startsWith("https://") ? "Le site est servi en HTTPS" : "Le site n’utilise pas HTTPS",
        details: homeDiagnostics.finalUrl,
        critical: false,
      }),
      buildStaticStep({
        id: "technical-accessibility-baseline",
        label: "Accessibilité de base",
        kind: "technical",
        status:
          homeDiagnostics.body.includes("<title>")
          && /<html[^>]+lang=/i.test(homeDiagnostics.body)
            ? "ok"
            : "warning",
        message:
          homeDiagnostics.body.includes("<title>")
          && /<html[^>]+lang=/i.test(homeDiagnostics.body)
            ? "Titre de page et langue du document détectés"
            : "Titre ou langue du document à confirmer",
        details: homeDiagnostics.finalUrl,
        critical: false,
      }),
      buildStaticStep({
        id: "technical-mobile-baseline",
        label: "Compatibilité mobile",
        kind: "technical",
        status: /<meta[^>]+name=["']viewport["']/i.test(homeDiagnostics.body) ? "ok" : "warning",
        message: /<meta[^>]+name=["']viewport["']/i.test(homeDiagnostics.body)
          ? "Viewport mobile détecté"
          : "Meta viewport absente ou non détectée",
        details: homeDiagnostics.finalUrl,
        critical: false,
      })
    );
  } else {
    steps.push(
      buildStaticStep({
        id: "technical-performance-home",
        label: "Performance de l’accueil",
        kind: "technical",
        status: "error",
        message: homeDiagnostics.error || "Impossible de mesurer l’accueil",
        details: homeDiagnostics.finalUrl,
        critical: false,
        durationMs: homeDiagnostics.durationMs,
      })
    );
  }

  steps.push(
    await fetchJsonCheck({
      id: "technical-api-public",
      label: "API publique",
      kind: "technical",
      url: `${baseUrl}/api/trpc/auth.providers?input=%7B%7D`,
      markers: ['"google"', '"facebook"'],
    })
  );

  let adminCookieHeader: string | null = isSameOrigin && incomingCookieHeader ? incomingCookieHeader : null;
  const hasConfiguredAdminAccount = Boolean(
    target.adminEmail &&
    (target.adminAuthMode === "oauth" || target.adminPassword)
  );
  if (!adminCookieHeader && hasConfiguredAdminAccount) {
    try {
      adminCookieHeader = await createConfiguredSessionCookie(target.adminEmail, target.adminAuthMode === "oauth" ? "" : target.adminPassword);
    } catch (error) {
      const usesOAuthOnly = target.adminAuthMode === "oauth";
      steps.push({
        id: "admin-config",
        label: "Configuration compte admin de contrôle",
        kind: "user",
        status: "warning",
        durationMs: 0,
        message: usesOAuthOnly ? "Impossible d’ouvrir une session admin OAuth" : "Impossible d’ouvrir une session admin dédiée",
        details: error instanceof Error ? error.message : usesOAuthOnly ? "Compte OAuth admin introuvable" : "Identifiants admin invalides",
        critical: false,
      });
    }
  }

  let associationCookieHeader: string | null = null;
  const hasConfiguredAssociationAccount = Boolean(
    target.associationEmail &&
    (target.associationAuthMode === "oauth" || target.associationPassword)
  );
  if (hasConfiguredAssociationAccount) {
    try {
      associationCookieHeader = await createConfiguredSessionCookie(
        target.associationEmail,
        target.associationAuthMode === "oauth" ? "" : target.associationPassword
      );
    } catch (error) {
      const usesOAuthOnly = target.associationAuthMode === "oauth";
      steps.push({
        id: "association-config",
        label: "Configuration compte association de test",
        kind: "user",
        status: "warning",
        durationMs: 0,
        message: usesOAuthOnly ? "Impossible d’ouvrir une session association OAuth" : "Impossible d’ouvrir une session association",
        details: error instanceof Error ? error.message : usesOAuthOnly ? "Compte OAuth association introuvable" : "Identifiants association invalides",
        critical: false,
      });
    }
  } else {
    steps.push({
      id: "association-config",
      label: "Configuration compte association de test",
      kind: "user",
      status: "warning",
      durationMs: 0,
      message: "Compte association non configuré",
      details: "Les contrôles authentifiés association sont ignorés tant que l’email et le mot de passe ne sont pas renseignés.",
      critical: false,
    });
  }

  let accueilCookieHeader: string | null = null;
  const hasConfiguredAccueilAccount = Boolean(
    target.accueilEmail &&
    (target.accueilAuthMode === "oauth" || target.accueilPassword)
  );
  if (hasConfiguredAccueilAccount) {
    try {
      accueilCookieHeader = await createConfiguredSessionCookie(
        target.accueilEmail,
        target.accueilAuthMode === "oauth" ? "" : target.accueilPassword
      );
    } catch (error) {
      const usesOAuthOnly = target.accueilAuthMode === "oauth";
      steps.push(buildStaticStep({
        id: "accueil-config",
        label: "Configuration compte accueil / hôtesse",
        kind: "user",
        status: "warning",
        message: usesOAuthOnly ? "Impossible d’ouvrir une session accueil OAuth" : "Impossible d’ouvrir une session accueil",
        details: error instanceof Error ? error.message : usesOAuthOnly ? "Compte OAuth accueil introuvable" : "Identifiants accueil invalides",
        critical: false,
      }));
    }
  } else {
    steps.push(buildStaticStep({
      id: "accueil-config",
      label: "Configuration compte accueil / hôtesse",
      kind: "user",
      status: "warning",
      message: "Compte accueil non configuré",
      details: "Le parcours hôtesse reste ignoré tant qu’un compte accueil n’est pas renseigné.",
      critical: false,
    }));
  }

  steps.push(
    await fetchPageCheck({ id: "technical-public-home", label: "Navigation publique • Accueil", kind: "technical", url: `${baseUrl}/`, expectedPath: "/", markers: ["Portail Associations", "<title>Portail Associations</title>"] }),
    await fetchPageCheck({ id: "technical-public-login", label: "Navigation publique • Connexion", kind: "technical", url: `${baseUrl}/login`, expectedPath: "/login", markers: ["Connexion", "Se connecter"] }),
    await fetchPageCheck({ id: "technical-directory", label: "Annuaire public des associations", kind: "technical", url: `${baseUrl}/associations`, expectedPath: "/associations", markers: ["Annuaire des associations", "associationDirectory.listPortal"] }),
    await fetchPageCheck({ id: "technical-map", label: "Cartographie des associations", kind: "technical", url: `${baseUrl}/associations/carte`, expectedPath: "/associations/carte", markers: ["Cartographie interactive", "associationDirectory.listMap"] }),
  );

  const publicProfileUrl = await tryFindPublicProfileUrl(baseUrl);
  if (publicProfileUrl) {
    steps.push(
      await fetchPageCheck({
        id: "technical-public-profile",
        label: "Fiche publique association",
        kind: "technical",
        url: publicProfileUrl,
        expectedPath: new URL(publicProfileUrl).pathname,
        markers: ["Coordonnées", "actualités et publications"],
        critical: false,
      })
    );
  } else {
    steps.push(buildStaticStep({
      id: "technical-public-profile",
      label: "Fiche publique association",
      kind: "technical",
      status: "warning",
      durationMs: 0,
      message: "Aucune fiche publique détectée",
      details: "Le tour n’a pas trouvé de lien de fiche association dans l’annuaire public.",
      critical: false,
    }));
  }

  if (associationCookieHeader) {
    steps.push(
      await fetchJsonCheck({
        id: "technical-api-association",
        label: "API association",
        kind: "technical",
        url: `${baseUrl}/api/trpc/auth.me?input=%7B%7D`,
        cookieHeader: associationCookieHeader,
        markers: ['"email"', '"role"'],
      }),
      await fetchPageCheck({ id: "user-association-dashboard", label: "Parcours association • Tableau de bord", kind: "user", url: `${baseUrl}/dashboard`, cookieHeader: associationCookieHeader, expectedPath: "/dashboard", markers: ["Tableau de bord", "<title>Portail Associations</title>"] }),
      await fetchPageCheck({ id: "user-association-requests", label: "Parcours association • Mes demandes", kind: "user", url: `${baseUrl}/dashboard/requests`, cookieHeader: associationCookieHeader, expectedPath: "/dashboard/requests", markers: ["Mes demandes"] }),
      await fetchPageCheck({ id: "user-association-documents", label: "Parcours association • Mes documents", kind: "user", url: `${baseUrl}/dashboard/documents`, cookieHeader: associationCookieHeader, expectedPath: "/dashboard/documents", markers: ["Mes documents"] }),
      await fetchPageCheck({ id: "user-association-directory", label: "Parcours association • Annuaire", kind: "user", url: `${baseUrl}/dashboard/associations`, cookieHeader: associationCookieHeader, expectedPath: "/dashboard/associations", markers: ["Annuaire des associations"], critical: false }),
      await fetchPageCheck({ id: "user-association-form-salle", label: "Formulaire • Réservation de salle", kind: "user", url: `${baseUrl}/dashboard/reservation-salle`, cookieHeader: associationCookieHeader, expectedPath: "/dashboard/reservation-salle", markers: ["Réservation de salle", "Soumettre"] }),
      await fetchPageCheck({ id: "user-association-form-request", label: "Formulaire • Nouvelle demande", kind: "user", url: `${baseUrl}/dashboard/requests/new`, cookieHeader: associationCookieHeader, expectedPath: "/dashboard/requests/new", markers: ["Nouvelle demande", "Soumettre"] }),
      await fetchPageCheck({ id: "user-association-form-material", label: "Formulaire • Matériel événementiel", kind: "user", url: `${baseUrl}/dashboard/materiel-evenementiel`, cookieHeader: associationCookieHeader, expectedPath: "/dashboard/materiel-evenementiel", markers: ["matériel événementiel", "Soumettre"], critical: false }),
    );
  }

  if (accueilCookieHeader) {
    steps.push(
      await fetchJsonCheck({
        id: "technical-api-accueil",
        label: "API accueil / hôtesse",
        kind: "technical",
        url: `${baseUrl}/api/trpc/auth.me?input=%7B%7D`,
        cookieHeader: accueilCookieHeader,
        markers: ['"accueil"'],
        critical: false,
      }),
      await fetchPageCheck({ id: "user-accueil-dashboard", label: "Parcours hôtesse • Administration", kind: "user", url: `${baseUrl}/admin`, cookieHeader: accueilCookieHeader, expectedPath: "/admin", markers: ["Administration accueil", "<title>Portail Associations</title>"] }),
      await fetchPageCheck({ id: "user-accueil-requests", label: "Parcours hôtesse • Gestion des demandes", kind: "user", url: `${baseUrl}/admin?tab=requests`, cookieHeader: accueilCookieHeader, expectedPath: "/admin", markers: ["Gestion des demandes"] }),
      await fetchPageCheck({ id: "user-accueil-calendar", label: "Parcours hôtesse • Calendrier", kind: "user", url: `${baseUrl}/admin?tab=calendrier`, cookieHeader: accueilCookieHeader, expectedPath: "/admin", markers: ["Calendrier"] }),
      await fetchPageCheck({ id: "user-accueil-notifications", label: "Parcours hôtesse • Notifications", kind: "user", url: `${baseUrl}/admin?tab=notifications`, cookieHeader: accueilCookieHeader, expectedPath: "/admin", markers: ["Notifications"], critical: false }),
    );
  }

  if (adminCookieHeader) {
    steps.push(
      await fetchJsonCheck({
        id: "technical-api-admin",
        label: "API administration",
        kind: "technical",
        url: `${baseUrl}/api/internal/stats/reservations?period=month`,
        cookieHeader: adminCookieHeader,
        markers: ['"summary"', '"history"'],
      }),
      await fetchPageCheck({ id: "user-admin-dashboard", label: "Parcours administrateur • Administration", kind: "user", url: `${baseUrl}/admin`, cookieHeader: adminCookieHeader, expectedPath: "/admin", markers: ["Tableau de bord", "<title>Portail Associations</title>"] }),
      await fetchPageCheck({ id: "user-admin-requests", label: "Parcours administrateur • Gestion des demandes", kind: "user", url: `${baseUrl}/admin?tab=requests`, cookieHeader: adminCookieHeader, expectedPath: "/admin", markers: ["Gestion des demandes"] }),
      await fetchPageCheck({ id: "user-admin-calendar", label: "Parcours administrateur • Calendrier", kind: "user", url: `${baseUrl}/admin?tab=calendrier`, cookieHeader: adminCookieHeader, expectedPath: "/admin", markers: ["Calendrier"] }),
      await fetchPageCheck({ id: "user-admin-analytics", label: "Parcours administrateur • Statistiques", kind: "user", url: `${baseUrl}/admin?tab=analytics`, cookieHeader: adminCookieHeader, expectedPath: "/admin", markers: ["Statistiques d’activité", "Tour opérationnel du site"] }),
      await fetchPageCheck({ id: "user-admin-ccds-directory", label: "Parcours administrateur • Annuaire interne CCDS", kind: "user", url: `${baseUrl}/admin?tab=ccds-directory`, cookieHeader: adminCookieHeader, expectedPath: "/admin", markers: ["Annuaire interne CCDS"] }),
    );
  } else {
    steps.push(buildStaticStep({
      id: "admin-config",
      label: "Configuration session admin",
      kind: "user",
      status: "warning",
      durationMs: 0,
      message: "Aucune session admin disponible pour cet environnement",
      details: isSameOrigin
        ? "Le navigateur n’a pas transmis de cookie de session exploitable."
        : "Renseignez un compte admin dédié si vous voulez tester un autre environnement que celui où vous êtes connecté.",
      critical: false,
    }));
  }

  const finishedAt = new Date();
  const report: OperationalTourReport = {
    id: `tour-${finishedAt.getTime()}`,
    environment: params.environment,
    environmentLabel,
    baseUrl,
    status: computeReportStatus(steps),
    startedAt: startedAt.toISOString(),
    finishedAt: finishedAt.toISOString(),
    durationMs: finishedAt.getTime() - startedAt.getTime(),
    initiatedBy: {
      userId: params.currentUser.id,
      name: params.currentUser.name || params.currentUser.email || `Utilisateur #${params.currentUser.id}`,
      email: params.currentUser.email || undefined,
    },
    summary: summarizeSteps(steps),
    steps,
  };

  const nextHistory = [report, ...(await getOperationalTourHistory())];
  await saveOperationalTourHistory(nextHistory);

  return report;
}
