import type { Kysely } from "kysely";
import { sql } from "kysely";

import { currentTimestamp } from "../dialect-helpers.js";

/**
 * Authorization codes for OAuth 2.1 Authorization Code + PKCE flow.
 *
 * Used by MCP clients (Claude Desktop, VS Code, etc.) to authenticate
 * via the standard OAuth authorization code grant.
 *
 * Also adds client_id tracking to oauth_tokens for per-client revocation.
 */
export async function up(db: Kysely<unknown>): Promise<void> {
	await db.schema
		.createTable("_emdash_authorization_codes")
		.addColumn("code_hash", "text", (col) => col.primaryKey()) // SHA-256 hash of authorization code
		.addColumn("client_id", "text", (col) => col.notNull()) // CIMD URL or opaque string
		.addColumn("redirect_uri", "text", (col) => col.notNull()) // Must match exactly on exchange
		.addColumn("user_id", "text", (col) => col.notNull())
		.addColumn("scopes", "text", (col) => col.notNull()) // JSON array
		.addColumn("code_challenge", "text", (col) => col.notNull()) // S256 challenge
		.addColumn("code_challenge_method", "text", (col) => col.notNull().defaultTo("S256"))
		.addColumn("resource", "text") // RFC 8707 resource indicator
		.addColumn("expires_at", "text", (col) => col.notNull())
		.addColumn("created_at", "text", (col) => col.defaultTo(currentTimestamp(db)))
		.addForeignKeyConstraint("auth_codes_user_fk", ["user_id"], "users", ["id"], (cb) =>
			cb.onDelete("cascade"),
		)
		.execute();

	await db.schema
		.createIndex("idx_auth_codes_expires")
		.on("_emdash_authorization_codes")
		.column("expires_at")
		.execute();

	// Track which client obtained a token (for per-client revocation)
	await sql`ALTER TABLE _emdash_oauth_tokens ADD COLUMN client_id TEXT`.execute(db);
}

export async function down(db: Kysely<unknown>): Promise<void> {
	await db.schema.dropTable("_emdash_authorization_codes").execute();
	// SQLite doesn't support DROP COLUMN, but this is only for dev rollback
}
