CREATE TABLE "user_email_identities" (
	"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
	"user_id" uuid NOT NULL,
	"email" varchar(255) NOT NULL,
	"verified_at" timestamp with time zone,
	"source" varchar(30) NOT NULL,
	"created_at" timestamp with time zone DEFAULT now() NOT NULL,
	"last_used_at" timestamp with time zone
);
--> statement-breakpoint
ALTER TABLE "user_email_identities" ADD CONSTRAINT "user_email_identities_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE INDEX "user_email_identities_user_id_idx" ON "user_email_identities" USING btree ("user_id");--> statement-breakpoint
CREATE INDEX "user_email_identities_email_idx" ON "user_email_identities" USING btree ("email");--> statement-breakpoint
CREATE UNIQUE INDEX "user_email_identities_user_id_email_unique" ON "user_email_identities" USING btree ("user_id","email");--> statement-breakpoint
CREATE UNIQUE INDEX "user_email_identities_verified_email_unique" ON "user_email_identities" USING btree ("email") WHERE "user_email_identities"."verified_at" IS NOT NULL;--> statement-breakpoint
-- Backfill: every existing user gets a `source='primary'` identity with
-- their `users.email` (lowercased). We trust existing rows because they
-- came in via our own sign-up/credentials flow, so `verified_at` is set
-- to `created_at`. This is what makes the new
-- `ensureUserIdByVerifiedEmail` lookup return the same `users.id` that
-- `ensureUserIdByEmail` used to return for every pre-existing user.
INSERT INTO "user_email_identities" ("user_id", "email", "verified_at", "source", "created_at")
SELECT "id", lower("email"), "created_at", 'primary', "created_at" FROM "users"
ON CONFLICT ("user_id", "email") DO NOTHING;