import { db } from "@server/db/pg/driver";
import { sql } from "drizzle-orm";
import { __DIRNAME, APP_PATH } from "@server/lib/consts";
import { readFileSync } from "fs";
import path, { join } from "path";

const version = "1.10.0";

export default async function migration() {
    console.log(`Running setup script ${version}...`);

    try {
        const resources = await db.execute(sql`
            SELECT "resourceId" FROM "resources"
        `);

        const siteResources = await db.execute(sql`
            SELECT "siteResourceId" FROM "siteResources"
        `);

        await db.execute(sql`BEGIN`);

        await db.execute(
            sql`ALTER TABLE "exitNodes" ADD COLUMN "region" text;`
        );

        await db.execute(
            sql`ALTER TABLE "idpOidcConfig" ADD COLUMN "variant" text DEFAULT 'oidc' NOT NULL;`
        );

        await db.execute(
            sql`ALTER TABLE "resources" ADD COLUMN "niceId" text DEFAULT '' NOT NULL;`
        );

        await db.execute(
            sql`ALTER TABLE "siteResources" ADD COLUMN "niceId" text DEFAULT '' NOT NULL;`
        );

        await db.execute(
            sql`ALTER TABLE "userOrgs" ADD COLUMN "autoProvisioned" boolean DEFAULT false;`
        );

        await db.execute(
            sql`ALTER TABLE "targets" ADD COLUMN "pathMatchType" text;`
        );

        await db.execute(sql`ALTER TABLE "targets" ADD COLUMN "path" text;`);

        await db.execute(
            sql`ALTER TABLE "resources" ADD COLUMN "headers" text;`
        );

        const usedNiceIds: string[] = [];

        for (const resource of resources.rows) {
            // Generate a unique name and ensure it's unique
            let niceId = "";
            let loops = 0;
            while (true) {
                if (loops > 100) {
                    throw new Error("Could not generate a unique name");
                }

                niceId = generateName();
                if (!usedNiceIds.includes(niceId)) {
                    usedNiceIds.push(niceId);
                    break;
                }
                loops++;
            }
            await db.execute(sql`
                UPDATE "resources" SET "niceId" = ${niceId} WHERE "resourceId" = ${resource.resourceId}
            `);
        }

        for (const resource of siteResources.rows) {
            // Generate a unique name and ensure it's unique
            let niceId = "";
            let loops = 0;
            while (true) {
                if (loops > 100) {
                    throw new Error("Could not generate a unique name");
                }

                niceId = generateName();
                if (!usedNiceIds.includes(niceId)) {
                    usedNiceIds.push(niceId);
                    break;
                }
                loops++;
            }
            await db.execute(sql`
                UPDATE "siteResources" SET "niceId" = ${niceId} WHERE "siteResourceId" = ${resource.siteResourceId}
            `);
        }

        // Handle auto-provisioned users for identity providers
        const autoProvisionIdps = await db.execute(sql`
            SELECT "idpId" FROM "idp" WHERE "autoProvision" = true
        `);

        for (const idp of autoProvisionIdps.rows) {
            // Get all users with this identity provider
            const usersWithIdp = await db.execute(sql`
                SELECT "id" FROM "user" WHERE "idpId" = ${idp.idpId}
            `);

            // Update userOrgs to set autoProvisioned to true for these users
            for (const user of usersWithIdp.rows) {
                await db.execute(sql`
                    UPDATE "userOrgs" SET "autoProvisioned" = true WHERE "userId" = ${user.id}
                `);
            }
        }

        await db.execute(sql`COMMIT`);
        console.log(`Migrated database`);
    } catch (e) {
        await db.execute(sql`ROLLBACK`);
        console.log("Failed to migrate db:", e);
        throw e;
    }
}

const dev = process.env.ENVIRONMENT !== "prod";
let file;
if (!dev) {
    file = join(__DIRNAME, "names.json");
} else {
    file = join("server/db/names.json");
}
export const names = JSON.parse(readFileSync(file, "utf-8"));

export function generateName(): string {
    const name = (
        names.descriptors[
            Math.floor(Math.random() * names.descriptors.length)
        ] +
        "-" +
        names.animals[Math.floor(Math.random() * names.animals.length)]
    )
        .toLowerCase()
        .replace(/\s/g, "-");

    // clean out any non-alphanumeric characters except for dashes
    return name.replace(/[^a-z0-9-]/g, "");
}
