import type { CheckResult, ClientSite, WPErrorType } from "../src/types.ts";
import { safeGet, probeSSL, type HttpPage } from "./network.ts";
import { InputError } from "./validation.ts";
export function detectWpError(
  html: string,
): { type: WPErrorType; message: string } | undefined {
  const clean = html.replace(
    /<!--[^]*?-->|<(script|style|pre|code)\b[^>]*>[^]*?<\/\1>/gi,
    " ",
  );
  const headings = [
    ...clean.matchAll(/<(title|h1|h2)\b[^>]*>([^]*?)<\/\1>/gi),
  ].map((m) => m[2].replace(/<[^>]+>/g, " ").trim());
  const text = clean
    .replace(/<[^>]+>/g, " ")
    .replace(/\s+/g, " ")
    .trim();
  const candidates = [...headings, ...(text.length < 2500 ? [text] : [])];
  const patterns: [RegExp, WPErrorType, string][] = [
    [
      /^error establishing a database connection(?:[.!\s]|$)/i,
      "database",
      "Halaman memaparkan ralat sambungan database. Punca sebenar perlu disahkan pada server.",
    ],
    [
      /^(?:there has been a critical error on (?:this|your) website|terdapat ralat kritikal)/i,
      "critical",
      "Halaman memaparkan ralat kritikal WordPress. Semak log PHP untuk mengenal pasti punca.",
    ],
    [
      /^briefly unavailable for scheduled maintenance/i,
      "maintenance",
      "Halaman memaparkan mod maintenance WordPress.",
    ],
    [
      /^(?:fatal error:|parse error:)/i,
      "php_fatal",
      "Halaman memaparkan ralat PHP. Semak log server untuk diagnosis.",
    ],
  ];
  for (const [re, type, message] of patterns)
    if (candidates.some((c) => re.test(c))) return { type, message };
}
export function restEndpoint(
  site: Pick<ClientSite, "url" | "restApiUrl">,
  finalUrl = site.url,
): string {
  if (site.restApiUrl) return site.restApiUrl;
  const base = new URL(finalUrl);
  base.search = "";
  base.hash = "";
  base.pathname = base.pathname.replace(/\/?$/, "/") + "wp-json/";
  return base.toString();
}
export function validWordPressJson(body: string): boolean {
  try {
    const data = JSON.parse(body);
    return (
      !!data &&
      typeof data === "object" &&
      Array.isArray(data.namespaces) &&
      data.namespaces.some(
        (n: unknown) => typeof n === "string" && /^wp\//.test(n),
      ) &&
      typeof data.routes === "object" &&
      !!data.routes
    );
  } catch {
    return false;
  }
}
export type GetPage = (
  url: string,
  options?: { timeoutMs?: number; maxBytes?: number },
) => Promise<HttpPage>;
export async function inspectSite(
  site: Pick<ClientSite, "url" | "restApiUrl">,
  get: GetPage = safeGet,
): Promise<CheckResult> {
  const started = Date.now();
  const hostname = new URL(site.url).hostname;
  const result: CheckResult = {
    url: site.url,
    hostname,
    status: "unknown",
    statusCode: 0,
    statusText: "Belum disahkan",
    responseTimeMs: 0,
    isWordPress: false,
    wpErrorType: null,
    restApiStatus: "unknown",
    ssl: { valid: false, checked: false },
    warnings: [],
    checkedAt: new Date().toISOString(),
  };
  try {
    const page = await get(site.url);
    result.finalUrl = page.finalUrl;
    result.statusCode = page.status;
    result.statusText = `HTTP ${page.status}`;
    result.responseTimeMs = page.elapsedMs;
    result.ssl = page.ssl;
    result.isWordPress =
      /\/(?:wp-content|wp-includes)\/|wp-block-|name=["']generator["']\s+content=["']WordPress/i.test(
        page.body,
      );
    result.wpVersion = page.body.match(
      /content=["']WordPress\s+([\d.]+)/i,
    )?.[1];
    const error = detectWpError(page.body);
    if (error) {
      result.wpErrorType = error.type;
      result.wpErrorMessage = error.message;
      result.isWordPress = true;
    } else if (page.status >= 500) {
      result.wpErrorType = "server_error";
      result.wpErrorMessage = `Server memulangkan HTTP ${page.status}. Punca perlu disahkan.`;
    } else if (page.status >= 400) {
      result.wpErrorMessage = `HTTP ${page.status}: halaman tidak tersedia atau akses disekat.`;
    }
    result.status =
      page.status >= 500 || error?.type === "database"
        ? "down"
        : error || page.status >= 400
          ? "error"
          : page.status >= 200 && page.status < 300
            ? "online"
            : "warning";
    if (page.status === 200 && !error) {
      try {
        const rest = await get(restEndpoint(site, page.finalUrl), {
          timeoutMs: 6000,
          maxBytes: 1048576,
        });
        result.restApiCode = rest.status;
        if (rest.status === 200 && validWordPressJson(rest.body)) {
          result.restApiStatus = "healthy";
          result.isWordPress = true;
        } else if (rest.status === 401 || rest.status === 403)
          result.restApiStatus = "blocked";
        else result.restApiStatus = result.isWordPress ? "error" : "unknown";
      } catch {
        result.restApiStatus = result.isWordPress ? "error" : "unknown";
      }
    }
    if (!result.ssl.valid)
      result.warnings!.push(result.ssl.error || "SSL tidak sah.");
    else if (
      result.ssl.daysRemaining !== undefined &&
      result.ssl.daysRemaining <= 14
    )
      result.warnings!.push(
        `SSL akan luput dalam ${result.ssl.daysRemaining} hari.`,
      );
    if (page.elapsedMs > 2500)
      result.warnings!.push("Masa respons melebihi 2.5 saat.");
    if (result.isWordPress && result.restApiStatus === "error")
      result.warnings!.push("REST API WordPress tidak dapat disahkan.");
    // A blocked public REST API may be intentional; it does not itself imply a broken site.
    if (result.status === "online" && result.warnings!.length)
      result.status = "warning";
    if (!result.wpErrorMessage)
      result.wpErrorMessage =
        result.warnings!.join(" ") || "Halaman boleh dicapai.";
  } catch (e) {
    const err = e as Error & { code?: string };
    result.responseTimeMs = Date.now() - started;
    if (
      e instanceof InputError ||
      ["ETOOLARGE", "EINSECUREREDIRECT"].includes(err.code || "")
    ) {
      result.status = "unknown";
      result.wpErrorType = "monitor_error";
      result.wpErrorMessage = err.message;
    } else {
      result.status = "down";
      const code = err.code || "";
      result.wpErrorType =
        code === "ETIMEDOUT"
          ? "timeout"
          : ["ENOTFOUND", "EAI_AGAIN"].includes(code)
            ? "dns_error"
            : /CERT|TLS|SSL|SELF_SIGNED|UNABLE_TO_VERIFY|DEPTH_ZERO/.test(code)
              ? "tls_error"
              : "connection_error";
      result.wpErrorMessage =
        result.wpErrorType === "tls_error"
          ? "Sambungan HTTPS gagal kerana sijil SSL."
          : result.wpErrorType === "timeout"
            ? "Website tidak memberi respons sebelum timeout."
            : result.wpErrorType === "dns_error"
              ? "DNS website tidak dapat diselesaikan."
              : "Sambungan ke website gagal.";
      if (result.wpErrorType === "tls_error" && get === safeGet) {
        try {
          result.ssl = await probeSSL(site.url);
        } catch {
          /* main failure retained */
        }
      }
    }
    result.statusText = result.wpErrorMessage || "Semakan gagal";
  }
  result.checkedAt = new Date().toISOString();
  return result;
}
