import { desc, eq, sql } from "drizzle-orm";

import { getDatabase } from "@/modules/core/server/db";
import {
  accounts,
  beds,
  joinRequests,
  memberships,
  organizationInvitations,
  organizations,
  roles,
  systemIncidents,
} from "@/modules/core/server/schema";
import type {
  Account,
  JoinRequest,
  Membership,
  Organization,
  OrganizationInvitation,
  SystemIncident,
} from "@/modules/core/types";

function iso(value: Date): string {
  return value.toISOString();
}

function toOrganization(row: typeof organizations.$inferSelect): Organization {
  return {
    id: row.id,
    name: row.name,
    slug: row.slug,
    status: row.status,
    registrationEnabled: row.registrationEnabled,
    oidcEnabled: row.oidcEnabled,
    oidcIssuerUrl: row.oidcIssuerUrl,
    oidcClientId: row.oidcClientId,
    oidcHasClientSecret: row.oidcClientSecret.length > 0,
    oidcScopes: row.oidcScopes,
    oidcRedirectUri: row.oidcRedirectUri,
    oidcAvatarClaim: row.oidcAvatarClaim,
    oidcAutoProvision: row.oidcAutoProvision,
    createdAt: iso(row.createdAt),
    updatedAt: iso(row.updatedAt),
  };
}

function toAccount(row: typeof accounts.$inferSelect): Account {
  return {
    id: row.id,
    platformRoleId: row.platformRoleId ?? undefined,
    name: row.name,
    email: row.email,
    avatarUrl: row.avatarUrl,
    role: row.platformRoleId ?? "viewer",
    passwordHash: row.passwordHash,
    passwordSalt: row.passwordSalt,
    status: row.status,
    createdAt: iso(row.createdAt),
    updatedAt: iso(row.updatedAt),
  };
}

function toMembership(row: typeof memberships.$inferSelect, role: typeof roles.$inferSelect): Membership {
  return {
    id: row.id,
    accountId: row.accountId,
    organizationId: row.organizationId,
    roleId: row.roleId,
    roleKey: role.key,
    roleLabel: role.label,
    status: row.status,
    createdAt: iso(row.createdAt),
    updatedAt: iso(row.updatedAt),
  };
}

function toJoinRequest(row: {
  accountEmail: string;
  accountName: string;
  organizationName: string;
  request: typeof joinRequests.$inferSelect;
}): JoinRequest {
  return {
    id: row.request.id,
    accountId: row.request.accountId,
    accountName: row.accountName,
    accountEmail: row.accountEmail,
    organizationId: row.request.organizationId,
    organizationName: row.organizationName,
    status: row.request.status,
    reason: row.request.reason,
    reviewedByAccountId: row.request.reviewedByAccountId ?? undefined,
    reviewedAt: row.request.reviewedAt ? iso(row.request.reviewedAt) : undefined,
    createdAt: iso(row.request.createdAt),
  };
}

function toOrganizationInvitation(row: {
  invitation: typeof organizationInvitations.$inferSelect;
  organizationName: string;
  roleLabel: string;
}): OrganizationInvitation {
  return {
    id: row.invitation.id,
    organizationId: row.invitation.organizationId,
    organizationName: row.organizationName,
    roleId: row.invitation.roleId,
    roleLabel: row.roleLabel,
    email: row.invitation.email,
    token: row.invitation.token,
    status: row.invitation.status as OrganizationInvitation["status"],
    createdByAccountId: row.invitation.createdByAccountId ?? undefined,
    acceptedByAccountId: row.invitation.acceptedByAccountId ?? undefined,
    acceptedAt: row.invitation.acceptedAt ? iso(row.invitation.acceptedAt) : undefined,
    expiresAt: iso(row.invitation.expiresAt),
    maxUses: row.invitation.maxUses,
    usedCount: row.invitation.usedCount,
    createdAt: iso(row.invitation.createdAt),
    updatedAt: iso(row.invitation.updatedAt),
  };
}

function toSystemIncident(row: typeof systemIncidents.$inferSelect): SystemIncident {
  return {
    id: row.id,
    organizationId: row.organizationId ?? undefined,
    severity: row.severity,
    status: row.status,
    title: row.title,
    description: row.description,
    source: row.source,
    createdAt: iso(row.createdAt),
    updatedAt: iso(row.updatedAt),
  };
}

export async function listSettingsOrganizations(params: { organizationId?: string } = {}): Promise<Organization[]> {
  const database = getDatabase();
  const rows = await database
    .select()
    .from(organizations)
    .where(params.organizationId ? eq(organizations.id, params.organizationId) : sql`true`)
    .orderBy(desc(organizations.createdAt));

  return rows.map(toOrganization);
}

export async function listSettingsAccounts(params: { organizationId?: string } = {}): Promise<Account[]> {
  const database = getDatabase();
  const rows = params.organizationId
    ? await database
        .select({ account: accounts })
        .from(accounts)
        .innerJoin(memberships, eq(memberships.accountId, accounts.id))
        .where(eq(memberships.organizationId, params.organizationId))
        .orderBy(desc(accounts.createdAt))
    : await database.select({ account: accounts }).from(accounts).orderBy(desc(accounts.createdAt));

  return rows.map((row) => toAccount(row.account));
}

export async function listSettingsMemberships(params: { organizationId?: string } = {}): Promise<Membership[]> {
  const database = getDatabase();
  const rows = await database
    .select({ membership: memberships, role: roles })
    .from(memberships)
    .innerJoin(roles, eq(roles.id, memberships.roleId))
    .where(params.organizationId ? eq(memberships.organizationId, params.organizationId) : sql`true`)
    .orderBy(desc(memberships.createdAt));

  return rows.map((row) => toMembership(row.membership, row.role));
}

export async function listSettingsJoinRequests(params: { organizationId?: string } = {}): Promise<JoinRequest[]> {
  const database = getDatabase();
  const rows = await database
    .select({
      request: joinRequests,
      accountName: accounts.name,
      accountEmail: accounts.email,
      organizationName: organizations.name,
    })
    .from(joinRequests)
    .innerJoin(accounts, eq(accounts.id, joinRequests.accountId))
    .innerJoin(organizations, eq(organizations.id, joinRequests.organizationId))
    .where(params.organizationId ? eq(joinRequests.organizationId, params.organizationId) : sql`true`)
    .orderBy(desc(joinRequests.createdAt));

  return rows.map(toJoinRequest);
}

export async function listSettingsOrganizationInvitations(
  params: { organizationId?: string } = {},
): Promise<OrganizationInvitation[]> {
  const database = getDatabase();
  const rows = await database
    .select({
      invitation: organizationInvitations,
      organizationName: organizations.name,
      roleLabel: roles.label,
    })
    .from(organizationInvitations)
    .innerJoin(organizations, eq(organizations.id, organizationInvitations.organizationId))
    .innerJoin(roles, eq(roles.id, organizationInvitations.roleId))
    .where(params.organizationId ? eq(organizationInvitations.organizationId, params.organizationId) : sql`true`)
    .orderBy(desc(organizationInvitations.createdAt));

  return rows.map(toOrganizationInvitation);
}

export async function listSettingsIncidents(params: { organizationId?: string } = {}): Promise<SystemIncident[]> {
  const database = getDatabase();
  const rows = await database
    .select()
    .from(systemIncidents)
    .where(params.organizationId ? eq(systemIncidents.organizationId, params.organizationId) : sql`true`)
    .orderBy(desc(systemIncidents.createdAt));

  return rows.map(toSystemIncident);
}

export async function countSettingsBeds(params: { organizationId?: string } = {}): Promise<number> {
  const database = getDatabase();
  const rows = await database
    .select({ count: sql<number>`count(*)::int` })
    .from(beds)
    .where(params.organizationId ? eq(beds.organizationId, params.organizationId) : sql`true`);

  return rows[0]?.count ?? 0;
}
