import { randomUUID } from "node:crypto";

import type { Pool } from "pg";

import { allowedAgentScopes, type AgentScope } from "../domain/scopes.js";
import { hashAgentSetupCode, hashAgentToken } from "../security/agent-access.js";

const allowedScopeSet = new Set<string>(allowedAgentScopes);

export type AgentStatus = "active" | "disabled" | "revoked";
export type AgentGrantMode = "voluntary" | "reporting";
export type AgentSetupCodeStatus = "active" | "used" | "expired" | "revoked";
export type AgentTokenStatus = "active" | "revoked" | "expired";
export type AgentTokenGrantScope = "agent" | "token";
export type AgentTokenPurpose = "ops" | "ontology";

export type AgentRecord = {
  id: string;
  ownerUserId: string;
  ownerEmail: string | null;
  displayName: string;
  avatarUrl: string | null;
  status: AgentStatus;
  createdAt: string;
  updatedAt: string;
};

export type AgentGrantRecord = {
  id: string;
  agentId: string;
  workspaceSlug: string;
  projectId: string | null;
  scopes: AgentScope[];
  mode: AgentGrantMode;
  createdByUserId: string;
  createdAt: string;
  updatedAt: string;
};

export type AgentTokenRecord = {
  id: string;
  agentId: string;
  name: string;
  status: AgentTokenStatus;
  purpose: AgentTokenPurpose;
  grantScope: AgentTokenGrantScope;
  tokenSuffix: string | null;
  expiresAt: string | null;
  lastUsedAt: string | null;
  createdAt: string;
};

export type AgentSessionRecord = {
  agent: AgentRecord;
  token: AgentTokenRecord;
  grants: AgentGrantRecord[];
  grantSource: AgentTokenGrantScope;
};

export type AgentSetupCodeRecord = {
  id: string;
  agentId: string;
  codeSuffix: string | null;
  status: AgentSetupCodeStatus;
  tokenName: string;
  createdByUserId: string | null;
  expiresAt: string;
  usedAt: string | null;
  usedTokenId: string | null;
  usedOntologyTokenId: string | null;
  createdAt: string;
};

type AgentRow = {
  id: string;
  owner_user_id: string;
  owner_email: string | null;
  display_name: string;
  avatar_url: string | null;
  status: AgentStatus;
  created_at: Date;
  updated_at: Date;
};

type GrantRow = {
  id: string;
  agent_id: string;
  workspace_slug: string;
  project_id: string;
  scopes: AgentScope[];
  mode: AgentGrantMode;
  created_by_user_id: string;
  created_at: Date;
  updated_at: Date;
};

type TokenRow = {
  id: string;
  agent_id: string;
  name: string;
  status: AgentTokenStatus;
  purpose: AgentTokenPurpose;
  grant_scope: AgentTokenGrantScope;
  token_suffix: string | null;
  expires_at: Date | null;
  last_used_at: Date | null;
  created_at: Date;
};

type SetupCodeRow = {
  id: string;
  agent_id: string;
  code_hash: string;
  code_suffix: string | null;
  status: AgentSetupCodeStatus;
  token_name: string;
  created_by_user_id: string | null;
  expires_at: Date;
  used_at: Date | null;
  used_token_id: string | null;
  used_ontology_token_id: string | null;
  created_at: Date;
};

type SessionRow = AgentRow & {
  token_id: string;
  token_name: string;
  token_status: AgentTokenStatus;
  token_purpose: AgentTokenPurpose;
  token_grant_scope: AgentTokenGrantScope;
  token_suffix: string | null;
  token_expires_at: Date | null;
  token_last_used_at: Date | null;
  token_created_at: Date;
};

type TokenGrantRow = {
  id: string;
  token_id: string;
  agent_id: string;
  workspace_slug: string;
  project_id: string;
  scopes: AgentScope[];
  mode: AgentGrantMode;
  created_by_user_id: string;
  source_grant_id: string | null;
  created_at: Date;
  updated_at: Date;
};

type IdempotencyRow = {
  key: string;
  agent_id: string;
  request_hash: string;
  response_body: unknown | null;
  status: "processing" | "completed";
  created_at: Date;
  expires_at: Date;
  locked_until: Date | null;
};

export type CreateAgentInput = {
  ownerUserId: string;
  ownerEmail?: string | null;
  displayName: string;
  avatarUrl?: string | null;
};

export type UpdateAgentProfileInput = {
  displayName?: string;
  avatarUrl?: string | null;
  actorUserId?: string;
};

export type UpsertGrantInput = {
  workspaceSlug: string;
  projectId?: string | null;
  scopes: AgentScope[];
  mode: AgentGrantMode;
  createdByUserId: string;
};

export type ReplaceWorkspaceGrantsInput = {
  workspaceSlug: string;
  projectIds: string[];
  scopes: AgentScope[];
  mode: AgentGrantMode;
  actorUserId: string;
};

export type ReplaceProjectGrantTarget = {
  workspaceSlug: string;
  projectId: string;
};

export type ReplaceProjectGrantsInput = {
  grants: ReplaceProjectGrantTarget[];
  scopes: AgentScope[];
  mode: AgentGrantMode;
  actorUserId: string;
};

export type CreateTokenInput = {
  token: string;
  name: string;
  purpose?: AgentTokenPurpose;
  expiresAt?: string | null;
  tokenGrants?: CreateTokenGrantInput[];
};

export type CreateSetupCodeInput = {
  code: string;
  createdByUserId?: string | null;
  expiresAt: string;
  tokenName: string;
};

export type CreateTokenGrantInput = {
  workspaceSlug: string;
  projectId?: string | null;
  scopes: AgentScope[];
  mode: AgentGrantMode;
  createdByUserId: string;
  sourceGrantId?: string | null;
};

export type IdempotencyClaim =
  | {
      status: "claimed";
    }
  | {
      status: "replay";
      responseBody: unknown;
    }
  | {
      status: "in_progress";
      lockedUntil: string | null;
    }
  | {
      status: "conflict";
    };

export type RedeemSetupCodeResult =
  | {
      status: "redeemed";
      agent: AgentRecord;
      token: AgentTokenRecord;
      ontologyToken: AgentTokenRecord;
      grants: AgentGrantRecord[];
      setupCode: AgentSetupCodeRecord;
    }
  | {
      status: "invalid" | "expired" | "used" | "revoked";
    };

export class AgentsRepository {
  constructor(private readonly pool: Pool) {}

  async createAgent(input: CreateAgentInput): Promise<AgentRecord> {
    const result = await this.pool.query<AgentRow>(
      `
        INSERT INTO agents(id, owner_user_id, owner_email, display_name, avatar_url)
        VALUES ($1, $2, $3, $4, $5)
        RETURNING *
      `,
      [randomUUID(), input.ownerUserId, input.ownerEmail ?? null, input.displayName, input.avatarUrl ?? null]
    );

    return mapAgent(result.rows[0]);
  }

  async listAgents(ownerUserId?: string): Promise<AgentRecord[]> {
    const result = ownerUserId
      ? await this.pool.query<AgentRow>("SELECT * FROM agents WHERE owner_user_id = $1 ORDER BY created_at DESC", [ownerUserId])
      : await this.pool.query<AgentRow>("SELECT * FROM agents ORDER BY created_at DESC");

    return result.rows.map(mapAgent);
  }

  async listAgentsByOwnerIdentity(ownerUserId?: string, ownerEmail?: string): Promise<AgentRecord[]> {
    const normalizedOwnerUserId = normalizeNullableText(ownerUserId);
    const normalizedOwnerEmail = normalizeNullableText(ownerEmail)?.toLowerCase();

    if (!normalizedOwnerUserId && !normalizedOwnerEmail) {
      return [];
    }

    const result = await this.pool.query<AgentRow>(
      `
        SELECT *
        FROM agents
        WHERE ($1::text IS NOT NULL AND owner_user_id = $1)
           OR ($2::text IS NOT NULL AND owner_email IS NOT NULL AND lower(owner_email) = $2)
        ORDER BY created_at DESC
      `,
      [normalizedOwnerUserId, normalizedOwnerEmail]
    );

    return result.rows.map(mapAgent);
  }

  async getAgent(agentId: string): Promise<AgentRecord | null> {
    const result = await this.pool.query<AgentRow>("SELECT * FROM agents WHERE id = $1", [agentId]);
    return result.rows[0] ? mapAgent(result.rows[0]) : null;
  }

  async updateAgentProfile(agentId: string, input: UpdateAgentProfileInput): Promise<AgentRecord | null> {
    const currentAgent = await this.getAgent(agentId);

    if (!currentAgent) {
      return null;
    }

    const displayName = input.displayName ?? currentAgent.displayName;
    const avatarUrl = input.avatarUrl === undefined ? currentAgent.avatarUrl : input.avatarUrl;
    const result = await this.pool.query<AgentRow>(
      `
        UPDATE agents
        SET display_name = $2, avatar_url = $3, updated_at = now()
        WHERE id = $1
        RETURNING *
      `,
      [agentId, displayName, avatarUrl]
    );

    await this.createAuditEvent(agentId, "agent.profile.updated", input.actorUserId, {
      displayName,
      avatarUrl,
    });

    return mapAgent(result.rows[0]);
  }

  async revokeAgent(agentId: string, actorUserId?: string): Promise<AgentRecord | null> {
    const client = await this.pool.connect();

    try {
      await client.query("BEGIN");
      const result = await client.query<AgentRow>(
        `
          UPDATE agents
          SET status = 'revoked', updated_at = now()
          WHERE id = $1
          RETURNING *
        `,
        [agentId]
      );

      if (!result.rows[0]) {
        await client.query("ROLLBACK");
        return null;
      }

      await client.query("UPDATE agent_tokens SET status = 'revoked' WHERE agent_id = $1 AND status = 'active'", [agentId]);
      await client.query(
        "INSERT INTO agent_audit_events(agent_id, event_type, actor_user_id) VALUES ($1, $2, $3)",
        [agentId, "agent.revoked", actorUserId ?? null]
      );
      await client.query("COMMIT");
      return mapAgent(result.rows[0]);
    } catch (error) {
      await client.query("ROLLBACK");
      throw error;
    } finally {
      client.release();
    }
  }

  async upsertGrant(agentId: string, input: UpsertGrantInput): Promise<AgentGrantRecord> {
    assertAllowedScopes(input.scopes);

    const projectId = input.projectId ?? "";
    const result = await this.pool.query<GrantRow>(
      `
        INSERT INTO agent_grants(agent_id, workspace_slug, project_id, scopes, mode, created_by_user_id)
        VALUES ($1, $2, $3, $4, $5, $6)
        ON CONFLICT (agent_id, workspace_slug, project_id)
        DO UPDATE SET
          scopes = EXCLUDED.scopes,
          mode = EXCLUDED.mode,
          created_by_user_id = EXCLUDED.created_by_user_id,
          updated_at = now()
        RETURNING *
      `,
      [agentId, input.workspaceSlug, projectId, input.scopes, input.mode, input.createdByUserId]
    );

    await this.createAuditEvent(agentId, "agent.grant.upserted", input.createdByUserId, {
      workspaceSlug: input.workspaceSlug,
      projectId: input.projectId ?? null,
      scopes: input.scopes,
      mode: input.mode,
    });

    return mapGrant(result.rows[0]);
  }

  async listGrants(agentId: string): Promise<AgentGrantRecord[]> {
    const result = await this.pool.query<GrantRow>("SELECT * FROM agent_grants WHERE agent_id = $1 ORDER BY created_at DESC", [agentId]);
    return result.rows.map(mapGrant);
  }

  async replaceWorkspaceProjectGrants(agentId: string, input: ReplaceWorkspaceGrantsInput): Promise<AgentGrantRecord[]> {
    assertAllowedScopes(input.scopes);

    const projectIds = [...new Set(input.projectIds.map((projectId) => projectId.trim()).filter(Boolean))];
    const client = await this.pool.connect();

    try {
      await client.query("BEGIN");
      await client.query(
        `
          DELETE FROM agent_grants
          WHERE agent_id = $1
            AND workspace_slug = $2
            AND NOT (project_id = ANY($3::text[]))
        `,
        [agentId, input.workspaceSlug, projectIds]
      );

      const grants: AgentGrantRecord[] = [];
      for (const projectId of projectIds) {
        const result = await client.query<GrantRow>(
          `
            INSERT INTO agent_grants(agent_id, workspace_slug, project_id, scopes, mode, created_by_user_id)
            VALUES ($1, $2, $3, $4, $5, $6)
            ON CONFLICT (agent_id, workspace_slug, project_id)
            DO UPDATE SET
              scopes = EXCLUDED.scopes,
              mode = EXCLUDED.mode,
              created_by_user_id = EXCLUDED.created_by_user_id,
              updated_at = now()
            RETURNING *
          `,
          [agentId, input.workspaceSlug, projectId, input.scopes, input.mode, input.actorUserId]
        );
        grants.push(mapGrant(result.rows[0]));
      }

      await client.query(
        `
          INSERT INTO agent_audit_events(agent_id, event_type, actor_user_id, metadata)
          VALUES ($1, $2, $3, $4)
        `,
        [
          agentId,
          "agent.grants.replaced",
          input.actorUserId,
          {
            workspaceSlug: input.workspaceSlug,
            projectIds,
            scopes: input.scopes,
            mode: input.mode,
          },
        ]
      );

      await client.query("COMMIT");
      return grants;
    } catch (error) {
      await client.query("ROLLBACK");
      throw error;
    } finally {
      client.release();
    }
  }

  async replaceProjectGrants(agentId: string, input: ReplaceProjectGrantsInput): Promise<AgentGrantRecord[]> {
    assertAllowedScopes(input.scopes);

    const seenKeys = new Set<string>();
    const grantTargets = input.grants
      .map((grant) => ({
        workspaceSlug: grant.workspaceSlug.trim(),
        projectId: grant.projectId.trim(),
      }))
      .filter((grant) => grant.workspaceSlug && grant.projectId)
      .filter((grant) => {
        const key = `${grant.workspaceSlug}:${grant.projectId}`;
        if (seenKeys.has(key)) {
          return false;
        }
        seenKeys.add(key);
        return true;
      });

    if (grantTargets.length === 0) {
      throw new Error("At least one project grant is required.");
    }

    const client = await this.pool.connect();

    try {
      await client.query("BEGIN");
      await client.query("DELETE FROM agent_grants WHERE agent_id = $1 AND project_id <> ''", [agentId]);

      const grants: AgentGrantRecord[] = [];
      for (const grantTarget of grantTargets) {
        const result = await client.query<GrantRow>(
          `
            INSERT INTO agent_grants(agent_id, workspace_slug, project_id, scopes, mode, created_by_user_id)
            VALUES ($1, $2, $3, $4, $5, $6)
            ON CONFLICT (agent_id, workspace_slug, project_id)
            DO UPDATE SET
              scopes = EXCLUDED.scopes,
              mode = EXCLUDED.mode,
              created_by_user_id = EXCLUDED.created_by_user_id,
              updated_at = now()
            RETURNING *
          `,
          [agentId, grantTarget.workspaceSlug, grantTarget.projectId, input.scopes, input.mode, input.actorUserId]
        );
        grants.push(mapGrant(result.rows[0]));
      }

      await client.query(
        `
          INSERT INTO agent_audit_events(agent_id, event_type, actor_user_id, metadata)
          VALUES ($1, $2, $3, $4)
        `,
        [
          agentId,
          "agent.project_grants.replaced",
          input.actorUserId,
          {
            grants: grantTargets,
            scopes: input.scopes,
            mode: input.mode,
          },
        ]
      );

      await client.query("COMMIT");
      return grants;
    } catch (error) {
      await client.query("ROLLBACK");
      throw error;
    } finally {
      client.release();
    }
  }

  async createToken(agentId: string, input: CreateTokenInput): Promise<AgentTokenRecord> {
    const tokenGrants = input.tokenGrants ?? [];
    const grantScope: AgentTokenGrantScope = tokenGrants.length > 0 ? "token" : "agent";
    const client = await this.pool.connect();
    let tokenRecord: AgentTokenRecord;

    for (const grant of tokenGrants) {
      assertAllowedScopes(grant.scopes);
    }

    try {
      await client.query("BEGIN");
      const result = await client.query<TokenRow>(
        `
          INSERT INTO agent_tokens(agent_id, token_hash, token_suffix, name, expires_at, grant_scope, purpose)
          VALUES ($1, $2, $3, $4, $5, $6, $7)
          RETURNING id, agent_id, name, status, purpose, grant_scope, token_suffix, expires_at, last_used_at, created_at
        `,
        [agentId, hashAgentToken(input.token), input.token.slice(-8), input.name, input.expiresAt ?? null, grantScope, input.purpose ?? "ops"]
      );
      tokenRecord = mapToken(result.rows[0]);

      for (const grant of tokenGrants) {
        await client.query(
          `
            INSERT INTO agent_token_grants(token_id, workspace_slug, project_id, scopes, mode, created_by_user_id, source_grant_id)
            VALUES ($1, $2, $3, $4, $5, $6, $7)
          `,
          [
            tokenRecord.id,
            grant.workspaceSlug,
            grant.projectId ?? "",
            grant.scopes,
            grant.mode,
            grant.createdByUserId,
            grant.sourceGrantId ?? null,
          ]
        );
      }

      await client.query("COMMIT");
    } catch (error) {
      await client.query("ROLLBACK");
      throw error;
    } finally {
      client.release();
    }

    await this.createAuditEvent(agentId, "agent.token.created", undefined, {
      tokenId: tokenRecord.id,
      name: input.name,
      expiresAt: input.expiresAt ?? null,
      purpose: input.purpose ?? "ops",
      grantScope,
      tokenGrantCount: tokenGrants.length,
    });

    return tokenRecord;
  }

  async createSetupCode(agentId: string, input: CreateSetupCodeInput): Promise<AgentSetupCodeRecord> {
    const result = await this.pool.query<SetupCodeRow>(
      `
        INSERT INTO agent_setup_codes(agent_id, code_hash, code_suffix, token_name, created_by_user_id, expires_at)
        VALUES ($1, $2, $3, $4, $5, $6)
        RETURNING *
      `,
      [
        agentId,
        hashAgentSetupCode(input.code),
        input.code.slice(-8),
        input.tokenName,
        input.createdByUserId ?? null,
        input.expiresAt,
      ]
    );
    const setupCode = mapSetupCode(result.rows[0]);

    await this.createAuditEvent(agentId, "agent.setup_code.created", input.createdByUserId ?? undefined, {
      setupCodeId: setupCode.id,
      codeSuffix: setupCode.codeSuffix,
      expiresAt: setupCode.expiresAt,
      tokenName: setupCode.tokenName,
    });

    return setupCode;
  }

  async redeemSetupCode(code: string, token: string, ontologyToken: string): Promise<RedeemSetupCodeResult> {
    const client = await this.pool.connect();

    try {
      await client.query("BEGIN");
      const setupCodeResult = await client.query<SetupCodeRow>(
        `
          SELECT *
          FROM agent_setup_codes
          WHERE code_hash = $1
          FOR UPDATE
        `,
        [hashAgentSetupCode(code)]
      );
      const setupCodeRow = setupCodeResult.rows[0];

      if (!setupCodeRow) {
        await client.query("COMMIT");
        return { status: "invalid" };
      }

      if (setupCodeRow.status === "used" || setupCodeRow.used_at) {
        await client.query("COMMIT");
        return { status: "used" };
      }

      if (setupCodeRow.status === "revoked") {
        await client.query("COMMIT");
        return { status: "revoked" };
      }

      if (setupCodeRow.status !== "active" || setupCodeRow.expires_at <= new Date()) {
        await client.query(
          `
            UPDATE agent_setup_codes
            SET status = 'expired'
            WHERE id = $1 AND status = 'active'
          `,
          [setupCodeRow.id]
        );
        await client.query(
          `
            INSERT INTO agent_audit_events(agent_id, event_type, actor_user_id, metadata)
            VALUES ($1, $2, $3, $4)
          `,
          [
            setupCodeRow.agent_id,
            "agent.setup_code.expired",
            setupCodeRow.created_by_user_id,
            {
              setupCodeId: setupCodeRow.id,
              codeSuffix: setupCodeRow.code_suffix,
            },
          ]
        );
        await client.query("COMMIT");
        return { status: "expired" };
      }

      const agentResult = await client.query<AgentRow>(
        `
          SELECT *
          FROM agents
          WHERE id = $1 AND status = 'active'
          FOR UPDATE
        `,
        [setupCodeRow.agent_id]
      );
      const agentRow = agentResult.rows[0];

      if (!agentRow) {
        await client.query("COMMIT");
        return { status: "revoked" };
      }

      const tokenResult = await client.query<TokenRow>(
        `
          INSERT INTO agent_tokens(agent_id, token_hash, token_suffix, name, expires_at, grant_scope, purpose)
          VALUES ($1, $2, $3, $4, NULL, 'agent', 'ops')
          RETURNING id, agent_id, name, status, purpose, grant_scope, token_suffix, expires_at, last_used_at, created_at
        `,
        [setupCodeRow.agent_id, hashAgentToken(token), token.slice(-8), setupCodeRow.token_name]
      );
      const tokenRow = tokenResult.rows[0];

      const ontologyTokenResult = await client.query<TokenRow>(
        `
          INSERT INTO agent_tokens(agent_id, token_hash, token_suffix, name, expires_at, grant_scope, purpose)
          VALUES ($1, $2, $3, $4, NULL, 'agent', 'ontology')
          RETURNING id, agent_id, name, status, purpose, grant_scope, token_suffix, expires_at, last_used_at, created_at
        `,
        [
          setupCodeRow.agent_id,
          hashAgentToken(ontologyToken),
          ontologyToken.slice(-8),
          `${setupCodeRow.token_name} (Ontology)`,
        ]
      );
      const ontologyTokenRow = ontologyTokenResult.rows[0];

      const redeemedSetupCodeResult = await client.query<SetupCodeRow>(
        `
          UPDATE agent_setup_codes
          SET status = 'used', used_at = now(), used_token_id = $2, used_ontology_token_id = $3
          WHERE id = $1
          RETURNING *
        `,
        [setupCodeRow.id, tokenRow.id, ontologyTokenRow.id]
      );

      const grantResult = await client.query<GrantRow>(
        "SELECT * FROM agent_grants WHERE agent_id = $1 ORDER BY created_at DESC",
        [setupCodeRow.agent_id]
      );

      await client.query(
        `
          INSERT INTO agent_audit_events(agent_id, event_type, actor_user_id, metadata)
          VALUES ($1, $2, $3, $4)
        `,
        [
          setupCodeRow.agent_id,
          "agent.setup_code.redeemed",
          setupCodeRow.created_by_user_id,
          {
            setupCodeId: setupCodeRow.id,
            codeSuffix: setupCodeRow.code_suffix,
            tokenId: tokenRow.id,
            ontologyTokenId: ontologyTokenRow.id,
            tokenName: tokenRow.name,
          },
        ]
      );

      await client.query("COMMIT");
      return {
        status: "redeemed",
        agent: mapAgent(agentRow),
        token: mapToken(tokenRow),
        ontologyToken: mapToken(ontologyTokenRow),
        grants: grantResult.rows.map(mapGrant),
        setupCode: mapSetupCode(redeemedSetupCodeResult.rows[0]),
      };
    } catch (error) {
      await client.query("ROLLBACK");
      throw error;
    } finally {
      client.release();
    }
  }

  async listTokens(agentId: string): Promise<AgentTokenRecord[]> {
    const result = await this.pool.query<TokenRow>(
      `
        SELECT id, agent_id, name, status, purpose, grant_scope, token_suffix, expires_at, last_used_at, created_at
        FROM agent_tokens
        WHERE agent_id = $1
        ORDER BY created_at DESC
      `,
      [agentId]
    );
    return result.rows.map(mapToken);
  }

  async revokeToken(agentId: string, tokenId: string, actorUserId?: string): Promise<AgentTokenRecord | null> {
    const result = await this.pool.query<TokenRow>(
      `
        UPDATE agent_tokens
        SET status = 'revoked'
        WHERE agent_id = $1 AND id = $2
        RETURNING id, agent_id, name, status, purpose, grant_scope, token_suffix, expires_at, last_used_at, created_at
      `,
      [agentId, tokenId]
    );

    if (!result.rows[0]) {
      return null;
    }

    await this.createAuditEvent(agentId, "agent.token.revoked", actorUserId, { tokenId });
    return mapToken(result.rows[0]);
  }

  async findActiveSessionByToken(token: string, purpose: AgentTokenPurpose = "ops"): Promise<AgentSessionRecord | null> {
    const result = await this.pool.query<SessionRow>(
      `
        SELECT
          agents.id,
          agents.owner_user_id,
          agents.owner_email,
          agents.display_name,
          agents.avatar_url,
          agents.status,
          agents.created_at,
          agents.updated_at,
          agent_tokens.id AS token_id,
          agent_tokens.name AS token_name,
          agent_tokens.status AS token_status,
          agent_tokens.purpose AS token_purpose,
          agent_tokens.grant_scope AS token_grant_scope,
          agent_tokens.token_suffix AS token_suffix,
          agent_tokens.expires_at AS token_expires_at,
          agent_tokens.last_used_at AS token_last_used_at,
          agent_tokens.created_at AS token_created_at
        FROM agent_tokens
        JOIN agents ON agents.id = agent_tokens.agent_id
        WHERE agent_tokens.token_hash = $1
          AND agent_tokens.purpose = $2
          AND agent_tokens.status = 'active'
          AND agents.status = 'active'
          AND (agent_tokens.expires_at IS NULL OR agent_tokens.expires_at > now())
      `,
      [hashAgentToken(token), purpose]
    );

    const row = result.rows[0];

    if (!row) {
      return null;
    }

    const updatedToken = await this.pool.query<TokenRow>(
      `
        UPDATE agent_tokens
        SET last_used_at = now()
        WHERE id = $1
        RETURNING id, agent_id, name, status, purpose, grant_scope, token_suffix, expires_at, last_used_at, created_at
      `,
      [row.token_id]
    );

    return {
      agent: mapAgent(row),
      token: mapToken(updatedToken.rows[0]),
      grants: row.token_grant_scope === "token" ? await this.listTokenGrants(row.token_id) : await this.listGrants(row.id),
      grantSource: row.token_grant_scope,
    };
  }

  async listTokenGrants(tokenId: string): Promise<AgentGrantRecord[]> {
    const result = await this.pool.query<TokenGrantRow>(
      `
        SELECT
          agent_token_grants.id,
          agent_token_grants.token_id,
          agent_tokens.agent_id,
          agent_token_grants.workspace_slug,
          agent_token_grants.project_id,
          ARRAY(
            SELECT token_scope
            FROM unnest(agent_token_grants.scopes) AS token_scope
            WHERE token_scope = ANY(COALESCE(agent_grants.scopes, agent_token_grants.scopes))
            ORDER BY token_scope
          ) AS scopes,
          COALESCE(agent_grants.mode, agent_token_grants.mode) AS mode,
          agent_token_grants.created_by_user_id,
          agent_token_grants.source_grant_id,
          agent_token_grants.created_at,
          agent_token_grants.updated_at
        FROM agent_token_grants
        JOIN agent_tokens ON agent_tokens.id = agent_token_grants.token_id
        LEFT JOIN agent_grants ON agent_grants.id = agent_token_grants.source_grant_id
        WHERE agent_token_grants.token_id = $1
          AND (agent_token_grants.source_grant_id IS NULL OR agent_grants.id IS NOT NULL)
        ORDER BY agent_token_grants.created_at DESC
      `,
      [tokenId]
    );

    return result.rows.map(mapTokenGrant);
  }

  async createAuditEvent(agentId: string | null, eventType: string, actorUserId?: string, metadata: Record<string, unknown> = {}): Promise<void> {
    await this.pool.query(
      `
        INSERT INTO agent_audit_events(agent_id, event_type, actor_user_id, metadata)
        VALUES ($1, $2, $3, $4)
      `,
      [agentId, eventType, actorUserId ?? null, metadata]
    );
  }

  async claimIdempotencyKey(agentId: string, idempotencyKey: string, requestHash: string): Promise<IdempotencyClaim> {
    const storageKey = buildIdempotencyStorageKey(agentId, idempotencyKey);
    const client = await this.pool.connect();

    try {
      await client.query("BEGIN");
      await client.query("DELETE FROM idempotency_keys WHERE key = $1 AND expires_at <= now()", [storageKey]);

      const inserted = await client.query<IdempotencyRow>(
        `
          INSERT INTO idempotency_keys(key, agent_id, request_hash, response_body, status, expires_at, locked_until)
          VALUES ($1, $2, $3, NULL, 'processing', now() + interval '24 hours', now() + interval '5 minutes')
          ON CONFLICT (key) DO NOTHING
          RETURNING *
        `,
        [storageKey, agentId, requestHash]
      );

      if (inserted.rows[0]) {
        await client.query("COMMIT");
        return { status: "claimed" };
      }

      const existing = await client.query<IdempotencyRow>("SELECT * FROM idempotency_keys WHERE key = $1 AND agent_id = $2 FOR UPDATE", [
        storageKey,
        agentId,
      ]);
      const row = existing.rows[0];

      if (!row) {
        await client.query("COMMIT");
        return { status: "conflict" };
      }

      if (row.request_hash !== requestHash) {
        await client.query("COMMIT");
        return { status: "conflict" };
      }

      if (row.status === "completed") {
        await client.query("COMMIT");
        return {
          status: "replay",
          responseBody: row.response_body,
        };
      }

      if (row.locked_until && row.locked_until > new Date()) {
        await client.query("COMMIT");
        return {
          status: "in_progress",
          lockedUntil: row.locked_until.toISOString(),
        };
      }

      await client.query(
        `
          UPDATE idempotency_keys
          SET locked_until = now() + interval '5 minutes'
          WHERE key = $1 AND agent_id = $2
        `,
        [storageKey, agentId]
      );
      await client.query("COMMIT");
      return { status: "claimed" };
    } catch (error) {
      await client.query("ROLLBACK");
      throw error;
    } finally {
      client.release();
    }
  }

  async completeIdempotencyKey(agentId: string, idempotencyKey: string, responseBody: unknown): Promise<void> {
    await this.pool.query(
      `
        UPDATE idempotency_keys
        SET response_body = $3, status = 'completed', locked_until = NULL
        WHERE key = $1 AND agent_id = $2
      `,
      [buildIdempotencyStorageKey(agentId, idempotencyKey), agentId, responseBody]
    );
  }

  async releaseIdempotencyKey(agentId: string, idempotencyKey: string): Promise<void> {
    await this.pool.query("DELETE FROM idempotency_keys WHERE key = $1 AND agent_id = $2 AND status = 'processing'", [
      buildIdempotencyStorageKey(agentId, idempotencyKey),
      agentId,
    ]);
  }
}

function buildIdempotencyStorageKey(agentId: string, idempotencyKey: string): string {
  return `${agentId}:${idempotencyKey}`;
}

function normalizeNullableText(value: string | undefined): string | null {
  const normalized = value?.trim();
  return normalized ? normalized : null;
}

function assertAllowedScopes(scopes: AgentScope[]): void {
  const deniedScope = scopes.find((scope) => !allowedScopeSet.has(scope));

  if (deniedScope) {
    throw new Error(`Unsupported agent scope: ${deniedScope}`);
  }
}

function mapAgent(row: AgentRow): AgentRecord {
  return {
    id: row.id,
    ownerUserId: row.owner_user_id,
    ownerEmail: row.owner_email,
    displayName: row.display_name,
    avatarUrl: row.avatar_url,
    status: row.status,
    createdAt: row.created_at.toISOString(),
    updatedAt: row.updated_at.toISOString(),
  };
}

function mapGrant(row: GrantRow): AgentGrantRecord {
  return {
    id: row.id,
    agentId: row.agent_id,
    workspaceSlug: row.workspace_slug,
    projectId: row.project_id === "" ? null : row.project_id,
    scopes: row.scopes,
    mode: row.mode,
    createdByUserId: row.created_by_user_id,
    createdAt: row.created_at.toISOString(),
    updatedAt: row.updated_at.toISOString(),
  };
}

function mapToken(row: TokenRow): AgentTokenRecord {
  return {
    id: row.id,
    agentId: row.agent_id,
    name: row.name,
    status: row.status,
    purpose: row.purpose,
    grantScope: row.grant_scope,
    tokenSuffix: row.token_suffix,
    expiresAt: row.expires_at?.toISOString() ?? null,
    lastUsedAt: row.last_used_at?.toISOString() ?? null,
    createdAt: row.created_at.toISOString(),
  };
}

function mapSetupCode(row: SetupCodeRow): AgentSetupCodeRecord {
  return {
    id: row.id,
    agentId: row.agent_id,
    codeSuffix: row.code_suffix,
    status: row.status,
    tokenName: row.token_name,
    createdByUserId: row.created_by_user_id,
    expiresAt: row.expires_at.toISOString(),
    usedAt: row.used_at?.toISOString() ?? null,
    usedTokenId: row.used_token_id,
    usedOntologyTokenId: row.used_ontology_token_id,
    createdAt: row.created_at.toISOString(),
  };
}

function mapTokenGrant(row: TokenGrantRow): AgentGrantRecord {
  return {
    id: row.id,
    agentId: row.agent_id,
    workspaceSlug: row.workspace_slug,
    projectId: row.project_id === "" ? null : row.project_id,
    scopes: row.scopes,
    mode: row.mode,
    createdByUserId: row.created_by_user_id,
    createdAt: row.created_at.toISOString(),
    updatedAt: row.updated_at.toISOString(),
  };
}
