-- ADR-0020: organization is the SaaS tenant root.
-- Backfill all existing data into one default organization so old databases
-- upgrade without a manual bootstrap step.

CREATE TYPE "OrganizationStatus" AS ENUM ('ACTIVE', 'SUSPENDED', 'ARCHIVED');
CREATE TYPE "OrganizationMemberRole" AS ENUM ('OWNER', 'ADMIN', 'MEMBER');

CREATE TABLE "Organization" (
  "id" TEXT NOT NULL,
  "slug" TEXT NOT NULL,
  "name" TEXT NOT NULL,
  "status" "OrganizationStatus" NOT NULL DEFAULT 'ACTIVE',
  "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
  "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,

  CONSTRAINT "Organization_pkey" PRIMARY KEY ("id")
);

CREATE UNIQUE INDEX "Organization_slug_key" ON "Organization"("slug");
CREATE INDEX "Organization_status_idx" ON "Organization"("status");

INSERT INTO "Organization" ("id", "slug", "name", "status")
VALUES ('org_default', 'legacy-default', 'Legacy Default Organization', 'ACTIVE');

CREATE TABLE "OrganizationMembership" (
  "id" TEXT NOT NULL,
  "organizationId" TEXT NOT NULL,
  "userId" TEXT NOT NULL,
  "role" "OrganizationMemberRole" NOT NULL DEFAULT 'MEMBER',
  "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
  "revokedAt" TIMESTAMP(3),

  CONSTRAINT "OrganizationMembership_pkey" PRIMARY KEY ("id")
);

INSERT INTO "OrganizationMembership" ("id", "organizationId", "userId", "role")
SELECT
  'orgmem_default_' || md5("User"."id"),
  'org_default',
  "User"."id",
  CASE
    WHEN EXISTS (
      SELECT 1 FROM "PlatformRoleAssignment"
      WHERE "PlatformRoleAssignment"."userId" = "User"."id"
        AND "PlatformRoleAssignment"."role" = 'ADMIN'
        AND "PlatformRoleAssignment"."revokedAt" IS NULL
    )
    THEN 'OWNER'::"OrganizationMemberRole"
    ELSE 'MEMBER'::"OrganizationMemberRole"
  END
FROM "User";

CREATE INDEX "OrganizationMembership_organizationId_revokedAt_idx" ON "OrganizationMembership"("organizationId", "revokedAt");
CREATE INDEX "OrganizationMembership_userId_revokedAt_idx" ON "OrganizationMembership"("userId", "revokedAt");
CREATE UNIQUE INDEX "OrganizationMembership_organizationId_userId_revokedAt_key" ON "OrganizationMembership"("organizationId", "userId", "revokedAt");
CREATE UNIQUE INDEX "OrganizationMembership_active_key" ON "OrganizationMembership"("organizationId", "userId") WHERE "revokedAt" IS NULL;

ALTER TABLE "OrganizationMembership" ADD CONSTRAINT "OrganizationMembership_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "OrganizationMembership" ADD CONSTRAINT "OrganizationMembership_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;

ALTER TABLE "Project" ADD COLUMN "organizationId" TEXT;
UPDATE "Project" SET "organizationId" = 'org_default';
ALTER TABLE "Project" ALTER COLUMN "organizationId" SET NOT NULL;
ALTER TABLE "Project" ADD CONSTRAINT "Project_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE;
CREATE INDEX "Project_organizationId_archivedAt_idx" ON "Project"("organizationId", "archivedAt");

ALTER TABLE "Team" ADD COLUMN "organizationId" TEXT;
UPDATE "Team" SET "organizationId" = 'org_default';
ALTER TABLE "Team" ALTER COLUMN "organizationId" SET NOT NULL;
ALTER TABLE "Team" ADD CONSTRAINT "Team_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE;
DROP INDEX IF EXISTS "Team_slug_key";
CREATE INDEX "Team_organizationId_archivedAt_idx" ON "Team"("organizationId", "archivedAt");
CREATE INDEX "Team_organizationId_slug_archivedAt_idx" ON "Team"("organizationId", "slug", "archivedAt");
CREATE UNIQUE INDEX "Team_active_slug_key" ON "Team"("organizationId", "slug") WHERE "archivedAt" IS NULL;

CREATE TABLE "ExternalDirectoryConnection" (
  "id" TEXT NOT NULL,
  "organizationId" TEXT NOT NULL,
  "provider" TEXT NOT NULL,
  "providerTenantId" TEXT,
  "source" TEXT NOT NULL,
  "status" TEXT NOT NULL DEFAULT 'ACTIVE',
  "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
  "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
  "revokedAt" TIMESTAMP(3),

  CONSTRAINT "ExternalDirectoryConnection_pkey" PRIMARY KEY ("id")
);

INSERT INTO "ExternalDirectoryConnection" ("id", "organizationId", "provider", "source")
SELECT DISTINCT
  'extconn_default_' || md5("source"),
  'org_default',
  'FEISHU',
  "source"
FROM "ExternalPrincipalMembership";

CREATE UNIQUE INDEX "ExternalDirectoryConnection_organizationId_provider_source_key" ON "ExternalDirectoryConnection"("organizationId", "provider", "source");
CREATE INDEX "ExternalDirectoryConnection_organizationId_revokedAt_idx" ON "ExternalDirectoryConnection"("organizationId", "revokedAt");
CREATE INDEX "ExternalDirectoryConnection_provider_providerTenantId_idx" ON "ExternalDirectoryConnection"("provider", "providerTenantId");
ALTER TABLE "ExternalDirectoryConnection" ADD CONSTRAINT "ExternalDirectoryConnection_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE;

DROP INDEX IF EXISTS "ExternalPrincipalMembership_userId_principalType_principalId_source_revokedAt_key";
DROP INDEX IF EXISTS "ExternalPrincipalMembership_active_key";
DROP INDEX IF EXISTS "ExternalPrincipalMembership_source_syncedAt_idx";

ALTER TABLE "ExternalPrincipalMembership" ADD COLUMN "connectionId" TEXT;
UPDATE "ExternalPrincipalMembership"
SET "connectionId" = 'extconn_default_' || md5("source");
ALTER TABLE "ExternalPrincipalMembership" ALTER COLUMN "connectionId" SET NOT NULL;
ALTER TABLE "ExternalPrincipalMembership" DROP COLUMN "source";
ALTER TABLE "ExternalPrincipalMembership" ADD CONSTRAINT "ExternalPrincipalMembership_connectionId_fkey" FOREIGN KEY ("connectionId") REFERENCES "ExternalDirectoryConnection"("id") ON DELETE CASCADE ON UPDATE CASCADE;

CREATE INDEX "ExternalPrincipalMembership_connectionId_revokedAt_idx" ON "ExternalPrincipalMembership"("connectionId", "revokedAt");
CREATE UNIQUE INDEX "ExternalPrincipalMembership_userId_principalType_principalId_connectionId_revokedAt_key" ON "ExternalPrincipalMembership"("userId", "principalType", "principalId", "connectionId", "revokedAt");
CREATE UNIQUE INDEX "ExternalPrincipalMembership_active_key" ON "ExternalPrincipalMembership"("userId", "principalType", "principalId", "connectionId") WHERE "revokedAt" IS NULL;

-- A principal should have one active role on a resource. Keep the strongest
-- grant and revoke lower active grants to preserve history without ambiguity.
DROP INDEX IF EXISTS "PermissionGrant_active_key";

WITH ranked_permission_grants AS (
  SELECT
    "id",
    row_number() OVER (
      PARTITION BY "resourceType", "resourceId", "principalType", "principalId"
      ORDER BY
        CASE "role"
          WHEN 'MANAGE' THEN 3
          WHEN 'EDIT' THEN 2
          WHEN 'READ' THEN 1
        END DESC,
        "createdAt" ASC,
        "id" ASC
    ) AS rn
  FROM "PermissionGrant"
  WHERE "revokedAt" IS NULL
)
UPDATE "PermissionGrant"
SET "revokedAt" = CURRENT_TIMESTAMP
WHERE "id" IN (
  SELECT "id" FROM ranked_permission_grants WHERE rn > 1
);

CREATE UNIQUE INDEX "PermissionGrant_active_principal_key" ON "PermissionGrant"("resourceType", "resourceId", "principalType", "principalId") WHERE "revokedAt" IS NULL;
