import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
import { InferSelectModel } from "drizzle-orm";

// Orgs table
export const orgs = sqliteTable("orgs", {
    orgId: integer("orgId").primaryKey({ autoIncrement: true }),
    name: text("name").notNull(),
    domain: text("domain").notNull(),
});

// Sites table
export const sites = sqliteTable("sites", {
    siteId: integer("siteId").primaryKey({ autoIncrement: true }),
    orgId: integer("orgId").references(() => orgs.orgId, {
        onDelete: "cascade",
    }),
    exitNode: integer("exitNode").references(() => exitNodes.exitNodeId, {
        onDelete: "set null",
    }),
    name: text("name").notNull(),
    subdomain: text("subdomain"),
    pubKey: text("pubKey"),
    subnet: text("subnet"),
    megabytesIn: integer("bytesIn"),
    megabytesOut: integer("bytesOut"),
});

// Resources table
export const resources = sqliteTable("resources", {
    resourceId: text("resourceId", { length: 2048 }).primaryKey(),
    siteId: integer("siteId").references(() => sites.siteId, {
        onDelete: "cascade",
    }),
    orgId: integer("orgId").references(() => orgs.orgId, {
        onDelete: "cascade",
    }),
    name: text("name").notNull(),
    subdomain: text("subdomain"),
});

// Targets table
export const targets = sqliteTable("targets", {
    targetId: integer("targetId").primaryKey({ autoIncrement: true }),
    resourceId: text("resourceId").references(() => resources.resourceId, {
        onDelete: "cascade",
    }),
    ip: text("ip").notNull(),
    method: text("method").notNull(),
    port: integer("port").notNull(),
    protocol: text("protocol"),
    enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
});

// Exit Nodes table
export const exitNodes = sqliteTable("exitNodes", {
    exitNodeId: integer("exitNodeId").primaryKey({ autoIncrement: true }),
    name: text("name").notNull(),
    address: text("address").notNull(),
    privateKey: text("privateKey"),
    listenPort: integer("listenPort"),
});

// Routes table
export const routes = sqliteTable("routes", {
    routeId: integer("routeId").primaryKey({ autoIncrement: true }),
    exitNodeId: integer("exitNodeId").references(() => exitNodes.exitNodeId, {
        onDelete: "cascade",
    }),
    subnet: text("subnet").notNull(),
});

// Users table
export const users = sqliteTable("user", {
    id: text("id").primaryKey(), // has to be id not userId for lucia
    email: text("email").notNull().unique(),
    passwordHash: text("passwordHash").notNull(),
    twoFactorEnabled: integer("twoFactorEnabled", { mode: "boolean" })
        .notNull()
        .default(false),
    twoFactorSecret: text("twoFactorSecret"),
    emailVerified: integer("emailVerified", { mode: "boolean" })
        .notNull()
        .default(false),
    dateCreated: text("dateCreated").notNull(),
});

export const twoFactorBackupCodes = sqliteTable("twoFactorBackupCodes", {
    id: integer("id").primaryKey({ autoIncrement: true }),
    userId: text("userId")
        .notNull()
        .references(() => users.id, { onDelete: "cascade" }),
    codeHash: text("codeHash").notNull(),
});

// Sessions table
export const sessions = sqliteTable("session", {
    id: text("id").primaryKey(), // has to be id not sessionId for lucia
    userId: text("userId")
        .notNull()
        .references(() => users.id, { onDelete: "cascade" }),
    expiresAt: integer("expiresAt").notNull(),
});

export const userOrgs = sqliteTable("userOrgs", {
    userId: text("userId")
        .notNull()
        .references(() => users.id),
    orgId: integer("orgId")
        .notNull()
        .references(() => orgs.orgId),
    roleId: integer("roleId")
        .notNull()
        .references(() => roles.roleId),
});

export const emailVerificationCodes = sqliteTable("emailVerificationCodes", {
    id: integer("id").primaryKey({ autoIncrement: true }),
    userId: text("userId")
        .notNull()
        .references(() => users.id, { onDelete: "cascade" }),
    email: text("email").notNull(),
    code: text("code").notNull(),
    expiresAt: integer("expiresAt").notNull(),
});

export const passwordResetTokens = sqliteTable("passwordResetTokens", {
    id: integer("id").primaryKey({ autoIncrement: true }),
    userId: text("userId")
        .notNull()
        .references(() => users.id, { onDelete: "cascade" }),
    tokenHash: text("tokenHash").notNull(),
    expiresAt: integer("expiresAt").notNull(),
});

export const actions = sqliteTable("actions", {
    actionId: text("actionId").primaryKey(),
    name: text("name"),
    description: text("description"),
});

export const roles = sqliteTable("roles", {
    roleId: integer("roleId").primaryKey({ autoIncrement: true }),
    orgId: integer("orgId").references(() => orgs.orgId, { onDelete: "cascade" }),
    isSuperuserRole: integer("isSuperuserRole", { mode: "boolean" }),
    name: text("name").notNull(),
    description: text("description"),
});

export const roleActions = sqliteTable("roleActions", {
    roleId: integer("roleId")
        .notNull()
        .references(() => roles.roleId, { onDelete: "cascade" }),
    actionId: text("actionId")
        .notNull()
        .references(() => actions.actionId, { onDelete: "cascade" }),
    orgId: integer("orgId")
        .notNull()
        .references(() => orgs.orgId, { onDelete: "cascade" }),
});

export const userActions = sqliteTable("userActions", {
    userId: text("userId")
        .notNull()
        .references(() => users.id, { onDelete: "cascade" }),
    actionId: text("actionId")
        .notNull()
        .references(() => actions.actionId, { onDelete: "cascade" }),
    orgId: integer("orgId")
        .notNull()
        .references(() => orgs.orgId, { onDelete: "cascade" }),
});

export const roleSites = sqliteTable("roleSites", {
    roleId: integer("roleId")
        .notNull()
        .references(() => roles.roleId, { onDelete: "cascade" }),
    siteId: integer("siteId")
        .notNull()
        .references(() => sites.siteId, { onDelete: "cascade" }),
});

export const userSites = sqliteTable("userSites", {
    userId: text("userId")
        .notNull()
        .references(() => users.id, { onDelete: "cascade" }),
    siteId: integer("siteId")
        .notNull()
        .references(() => sites.siteId, { onDelete: "cascade" }),
});

export const roleResources = sqliteTable("roleResources", {
    roleId: integer("roleId")
        .notNull()
        .references(() => roles.roleId, { onDelete: "cascade" }),
    resourceId: text("resourceId")
        .notNull()
        .references(() => resources.resourceId, { onDelete: "cascade" }),
});

export const userResources = sqliteTable("userResources", {
    userId: text("userId")
        .notNull()
        .references(() => users.id, { onDelete: "cascade" }),
    resourceId: text("resourceId")
        .notNull()
        .references(() => resources.resourceId, { onDelete: "cascade" }),
});

export const limitsTable = sqliteTable("limits", {
    limitId: integer("limitId").primaryKey({ autoIncrement: true }),
    orgId: integer("orgId").references(() => orgs.orgId, {
        onDelete: "cascade",
    }),
    name: text("name").notNull(),
    value: integer("value").notNull(),
    description: text("description"),
});

// Define the model types for type inference
export type Org = InferSelectModel<typeof orgs>;
export type User = InferSelectModel<typeof users>;
export type Site = InferSelectModel<typeof sites>;
export type Resource = InferSelectModel<typeof resources>;
export type ExitNode = InferSelectModel<typeof exitNodes>;
export type Route = InferSelectModel<typeof routes>;
export type Target = InferSelectModel<typeof targets>;
export type Session = InferSelectModel<typeof sessions>;
export type EmailVerificationCode = InferSelectModel<
    typeof emailVerificationCodes
>;
export type TwoFactorBackupCode = InferSelectModel<typeof twoFactorBackupCodes>;
export type PasswordResetToken = InferSelectModel<typeof passwordResetTokens>;
export type Role = InferSelectModel<typeof roles>;
export type Action = InferSelectModel<typeof actions>;
export type RoleAction = InferSelectModel<typeof roleActions>;
export type UserAction = InferSelectModel<typeof userActions>;
export type RoleSite = InferSelectModel<typeof roleSites>;
export type UserSite = InferSelectModel<typeof userSites>;
export type RoleResource = InferSelectModel<typeof roleResources>;
export type UserResource = InferSelectModel<typeof userResources>;
export type Limit = InferSelectModel<typeof limitsTable>;