import type { Page, Route } from "@playwright/test";

export type ClientStatus = "active" | "inactive";
export type ClientType = "private" | "pkce";

export type Client = {
  id: string;
  name: string;
  type: ClientType;
  status: ClientStatus;
  redirectUris: string[];
  scopes: string[];
  createdAt: string;
  clientSecret?: string;
  tokenEndpointAuthMethod?: string;
  jwksUri?: string;
  jwks?: Record<string, unknown> | string;
  headlessJwksCache?: {
    clientId: string;
    jwksUri: string;
    cachedAt: string;
    expiresAt: string;
    lastCheckedAt?: string;
    lastSuccessfulVerificationAt?: string;
    lastRefreshStatus?: "success" | "failure" | "pending";
    lastError?: string;
    consecutiveFailures?: number;
    cachedKids?: string[];
    etag?: string;
    lastModified?: string;
    parsedKeys?: Array<{
      kid?: string;
      kty?: string;
      use?: string;
      alg?: string;
      n?: string;
    }>;
  };
  metadata?: Record<string, unknown>;
};

export type Consent = {
  subject: string;
  userName: string;
  clientId: string;
  clientName: string;
  grantedScopes: string[];
  authenticatedAt?: string;
  createdAt: string;
  deletedAt?: string;
  status: "active" | "revoked";
  tenantId: string;
  tenantName: string;
};

export type AuditLog = {
  event_id: string;
  timestamp: string;
  user_id: string;
  event_type: string;
  status: "success" | "failure";
  ip_address: string;
  user_agent: string;
  details: string;
};

export type DevApiMockState = {
  clients: Client[];
  consents: Consent[];
  auditLogsByCursor?: Record<
    string,
    { items: AuditLog[]; next_cursor?: string }
  >;
  auditLogs?: AuditLog[];
  onUpdateStatus?: (status: ClientStatus) => void;
  onRotateSecret?: (newSecret: string) => void;
  onRefreshHeadlessJwks?: (clientId: string) => void;
  onRevokeHeadlessJwksCache?: (clientId: string) => void;
};

export function makeClient(
  id: string,
  overrides: Partial<Client> = {},
): Client {
  return {
    id,
    name: `${id} app`,
    type: "private",
    status: "active",
    redirectUris: [`https://${id}.example.com/callback`],
    scopes: ["openid", "profile", "email"],
    createdAt: "2026-03-03T00:00:00.000Z",
    clientSecret: `${id}-secret`,
    metadata: {},
    ...overrides,
  };
}

export async function seedAuth(page: Page, role?: string) {
  const nowInSeconds = Math.floor(Date.now() / 1000);

  await page.addInitScript(
    ({ issuedAt, injectedRole }) => {
      const mockOidcUser = {
        id_token: "playwright-id-token",
        session_state: "playwright-session",
        access_token: "playwright-access-token",
        refresh_token: "playwright-refresh-token",
        token_type: "Bearer",
        scope: "openid profile email",
        profile: {
          sub: "playwright-user",
          email: "playwright@example.com",
          name: "Playwright User",
          ...(injectedRole ? { role: injectedRole } : {}),
        },
        expires_at: issuedAt + 3600,
      };

      window.localStorage.setItem(
        "oidc.user:http://localhost:5000/oidc:devfront",
        JSON.stringify(mockOidcUser),
      );
      window.localStorage.setItem(
        "oidc.user:http://localhost:5000/oidc/:devfront",
        JSON.stringify(mockOidcUser),
      );
      window.localStorage.setItem("dev_role", injectedRole || "rp_admin");
      window.localStorage.setItem("dev_tenant_id", "tenant-a");
    },
    { issuedAt: nowInSeconds, injectedRole: role ?? "" },
  );

  await page.route("**/oidc/**", async (route) => {
    const url = route.request().url();
    if (url.includes(".well-known/openid-configuration")) {
      await route.fulfill({
        json: {
          issuer: "http://localhost:5000/oidc",
          authorization_endpoint: "http://localhost:5000/oidc/auth",
          token_endpoint: "http://localhost:5000/oidc/token",
          jwks_uri: "http://localhost:5000/oidc/jwks",
          userinfo_endpoint: "http://localhost:5000/oidc/userinfo",
          end_session_endpoint: "http://localhost:5000/oidc/session/end",
        },
        headers: { "Access-Control-Allow-Origin": "*" },
      });
    } else if (url.includes("/jwks")) {
      await route.fulfill({
        json: { keys: [] },
        headers: { "Access-Control-Allow-Origin": "*" },
      });
    } else {
      await route.fulfill({
        status: 200,
        body: "ok",
        headers: { "Access-Control-Allow-Origin": "*" },
      });
    }
  });
}

function json(route: Route, payload: unknown, status = 200) {
  return route.fulfill({
    status,
    contentType: "application/json",
    body: JSON.stringify(payload),
  });
}

function parseClientId(pathname: string): string {
  const parts = pathname.split("/").filter(Boolean);
  return parts[parts.length - 1] ?? "";
}

export async function installDevApiMock(page: Page, state: DevApiMockState) {
  const appendAuditLog = (
    eventType: string,
    action: string,
    targetId: string,
    status: "success" | "failure" = "success",
  ) => {
    if (!state.auditLogs) return;
    state.auditLogs.unshift({
      event_id: `evt-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`,
      timestamp: new Date().toISOString(),
      user_id: "playwright-user",
      event_type: eventType,
      status,
      ip_address: "127.0.0.1",
      user_agent: "playwright",
      details: JSON.stringify({
        action,
        target_id: targetId,
        tenant_id: "tenant-a",
      }),
    });
  };

  await page.route("**/api/v1/user/me", async (route) => {
    const storedRole =
      (await page.evaluate(() => window.localStorage.getItem("dev_role"))) ??
      "rp_admin";
    return json(route, {
      id: "playwright-user",
      loginId: "playwright@example.com",
      email: "playwright@example.com",
      name: "Playwright User",
      phoneNumber: "",
      department: "QA",
      tenantId: "tenant-a",
      tenantName: "Tenant A",
      role: storedRole,
      createdAt: "2026-03-03T00:00:00.000Z",
      updatedAt: "2026-03-03T00:00:00.000Z",
    });
  });

  await page.route("**/api/v1/dev/**", async (route) => {
    const request = route.request();
    const url = new URL(request.url());
    const { pathname, searchParams } = url;
    const method = request.method();

    if (pathname === "/api/v1/dev/my-tenants" && method === "GET") {
      return json(route, [
        { id: "tenant-a", name: "Tenant A", slug: "tenant-a" },
      ]);
    }

    if (pathname === "/api/v1/dev/stats" && method === "GET") {
      const total = state.clients.length;
      return json(route, {
        total_clients: total,
        active_sessions: Math.max(1, total),
        auth_failures_24h: 0,
      });
    }

    if (pathname === "/api/v1/dev/clients" && method === "GET") {
      return json(route, {
        items: state.clients.map((client) => ({
          id: client.id,
          name: client.name,
          type: client.type,
          status: client.status,
          createdAt: client.createdAt,
          redirectUris: client.redirectUris,
          scopes: client.scopes,
        })),
        limit: 50,
        offset: 0,
      });
    }

    if (pathname === "/api/v1/dev/clients" && method === "POST") {
      const payload = (request.postDataJSON() as {
        name?: string;
        type?: ClientType;
        status?: ClientStatus;
        redirectUris?: string[];
        scopes?: string[];
        tokenEndpointAuthMethod?: string;
        jwksUri?: string;
        jwks?: Record<string, unknown> | string;
        metadata?: Record<string, unknown>;
      }) || { name: "created app" };

      const created = makeClient(`client-${state.clients.length + 1}`, {
        name: payload.name ?? "created app",
        type: payload.type ?? "private",
        status: payload.status ?? "active",
        redirectUris: payload.redirectUris ?? [],
        scopes: payload.scopes ?? ["openid"],
        tokenEndpointAuthMethod: payload.tokenEndpointAuthMethod,
        jwksUri: payload.jwksUri,
        jwks: payload.jwks,
        metadata: payload.metadata ?? {},
      });

      state.clients.push(created);
      appendAuditLog("CLIENT_CREATE", "CREATE_CLIENT", created.id);
      return json(route, {
        client: created,
        endpoints: {
          discovery: "https://issuer/.well-known/openid-configuration",
          issuer: "https://issuer",
          authorization: "https://issuer/oauth2/auth",
          token: "https://issuer/oauth2/token",
          userinfo: "https://issuer/userinfo",
        },
      });
    }

    if (
      pathname.startsWith("/api/v1/dev/clients/") &&
      pathname.endsWith("/status") &&
      method === "PATCH"
    ) {
      const clientId = pathname.split("/")[5] ?? "";
      const payload = request.postDataJSON() as { status: ClientStatus };
      const found = state.clients.find((client) => client.id === clientId);
      if (!found) return json(route, { error: "not found" }, 404);
      found.status = payload.status;
      appendAuditLog("CLIENT_UPDATE_STATUS", "UPDATE_CLIENT_STATUS", clientId);
      state.onUpdateStatus?.(payload.status);
      return json(route, {
        client: found,
        endpoints: {
          discovery: "https://issuer/.well-known/openid-configuration",
          issuer: "https://issuer",
          authorization: "https://issuer/oauth2/auth",
          token: "https://issuer/oauth2/token",
          userinfo: "https://issuer/userinfo",
        },
      });
    }

    if (
      pathname.startsWith("/api/v1/dev/clients/") &&
      pathname.endsWith("/secret/rotate") &&
      method === "POST"
    ) {
      const clientId = pathname.split("/")[5] ?? "";
      const found = state.clients.find((client) => client.id === clientId);
      if (!found) return json(route, { error: "not found" }, 404);
      found.clientSecret = `${clientId}-rotated-secret`;
      appendAuditLog("CLIENT_ROTATE_SECRET", "ROTATE_SECRET", clientId);
      state.onRotateSecret?.(found.clientSecret);
      return json(route, {
        client: found,
        endpoints: {
          discovery: "https://issuer/.well-known/openid-configuration",
          issuer: "https://issuer",
          authorization: "https://issuer/oauth2/auth",
          token: "https://issuer/oauth2/token",
          userinfo: "https://issuer/userinfo",
        },
      });
    }

    if (pathname.startsWith("/api/v1/dev/clients/") && method === "PUT") {
      const clientId = parseClientId(pathname);
      const payload = (request.postDataJSON() as {
        name?: string;
        type?: ClientType;
        scopes?: string[];
        redirectUris?: string[];
        tokenEndpointAuthMethod?: string;
        jwksUri?: string;
        jwks?: Record<string, unknown> | string;
        metadata?: Record<string, unknown>;
      }) || { name: "updated app" };
      const found = state.clients.find((client) => client.id === clientId);
      if (!found) return json(route, { error: "not found" }, 404);
      if (payload.name) found.name = payload.name;
      if (payload.type) found.type = payload.type;
      if (payload.scopes) found.scopes = payload.scopes;
      if (payload.redirectUris) found.redirectUris = payload.redirectUris;
      if (payload.tokenEndpointAuthMethod !== undefined) {
        found.tokenEndpointAuthMethod = payload.tokenEndpointAuthMethod;
      }
      if (payload.jwksUri !== undefined) {
        found.jwksUri = payload.jwksUri;
      }
      if (payload.jwks !== undefined) {
        found.jwks = payload.jwks;
      }
      if (payload.metadata) found.metadata = payload.metadata;
      appendAuditLog("CLIENT_UPDATE", "UPDATE_CLIENT", clientId);
      return json(route, {
        client: found,
        endpoints: {
          discovery: "https://issuer/.well-known/openid-configuration",
          issuer: "https://issuer",
          authorization: "https://issuer/oauth2/auth",
          token: "https://issuer/oauth2/token",
          userinfo: "https://issuer/userinfo",
        },
      });
    }

    if (pathname.startsWith("/api/v1/dev/clients/") && method === "GET") {
      const clientId = parseClientId(pathname);
      const found = state.clients.find((client) => client.id === clientId);
      if (!found) return json(route, { error: "forbidden" }, 403);
      return json(route, {
        client: found,
        endpoints: {
          discovery: "https://issuer/.well-known/openid-configuration",
          issuer: "https://issuer",
          authorization: "https://issuer/oauth2/auth",
          token: "https://issuer/oauth2/token",
          userinfo: "https://issuer/userinfo",
        },
        headlessJwksCache: found.headlessJwksCache,
      });
    }

    if (
      pathname.startsWith("/api/v1/dev/clients/") &&
      pathname.endsWith("/headless-jwks/cache") &&
      method === "DELETE"
    ) {
      const clientId = pathname.split("/")[5] ?? "";
      const found = state.clients.find((client) => client.id === clientId);
      if (!found) return json(route, { error: "not found" }, 404);
      found.headlessJwksCache = undefined;
      state.onRevokeHeadlessJwksCache?.(clientId);
      return route.fulfill({ status: 204 });
    }

    if (
      pathname.startsWith("/api/v1/dev/clients/") &&
      pathname.endsWith("/headless-jwks/refresh") &&
      method === "POST"
    ) {
      const clientId = pathname.split("/")[5] ?? "";
      const found = state.clients.find((client) => client.id === clientId);
      if (!found) return json(route, { error: "not found" }, 404);
      state.onRefreshHeadlessJwks?.(clientId);
      return json(route, {
        client: found,
        endpoints: {
          discovery: "https://issuer/.well-known/openid-configuration",
          issuer: "https://issuer",
          authorization: "https://issuer/oauth2/auth",
          token: "https://issuer/oauth2/token",
          userinfo: "https://issuer/userinfo",
        },
        headlessJwksCache: found.headlessJwksCache,
      });
    }

    if (
      pathname.startsWith("/api/v1/dev/clients/") &&
      !pathname.endsWith("/headless-jwks/cache") &&
      method === "DELETE"
    ) {
      const clientId = parseClientId(pathname);
      state.clients = state.clients.filter((client) => client.id !== clientId);
      appendAuditLog("CLIENT_DELETE", "DELETE_CLIENT", clientId);
      return route.fulfill({ status: 204 });
    }

    if (pathname === "/api/v1/dev/consents" && method === "GET") {
      const subject = searchParams.get("subject") || "";
      const clientId = searchParams.get("client_id") || "";
      const status = searchParams.get("status") || "";
      const items = state.consents.filter((row) => {
        const matchesSubject =
          !subject ||
          row.subject.includes(subject) ||
          row.userName.includes(subject);
        const matchesClientId = !clientId || row.clientId === clientId;
        const matchesStatus = !status || row.status === status;
        return matchesSubject && matchesClientId && matchesStatus;
      });
      return json(route, { items });
    }

    if (pathname === "/api/v1/dev/consents" && method === "DELETE") {
      const subject = searchParams.get("subject") || "";
      const clientId = searchParams.get("client_id") || "";
      const target = state.consents.find(
        (row) => row.subject === subject && row.clientId === clientId,
      );
      if (target) {
        target.status = "revoked";
        target.deletedAt = "2026-03-03T10:00:00.000Z";
      }
      return route.fulfill({ status: 204 });
    }

    if (pathname === "/api/v1/dev/audit-logs" && method === "GET") {
      if (state.auditLogsByCursor) {
        const cursor = searchParams.get("cursor") || "";
        const pageSet = state.auditLogsByCursor[cursor] ?? { items: [] };
        return json(route, {
          items: pageSet.items,
          limit: 50,
          cursor: cursor || undefined,
          next_cursor: pageSet.next_cursor,
        });
      }

      if (state.auditLogs) {
        const action = searchParams.get("action") || "";
        const clientId = searchParams.get("client_id") || "";
        const status = searchParams.get("status") || "";
        const filtered = state.auditLogs.filter((item) => {
          let parsedDetails: { action?: string; target_id?: string } = {};
          try {
            parsedDetails = JSON.parse(item.details) as {
              action?: string;
              target_id?: string;
            };
          } catch {}
          const matchesAction = !action || parsedDetails.action === action;
          const matchesClient =
            !clientId || parsedDetails.target_id === clientId;
          const matchesStatus = !status || item.status === status;
          return matchesAction && matchesClient && matchesStatus;
        });
        return json(route, { items: filtered, limit: 50 });
      }

      return json(route, { items: [], limit: 50 });
    }

    return json(route, { error: `Unhandled ${method} ${pathname}` }, 404);
  });
}
