import { IamAccount } from '@api/graphql';
import { IamFindAccountService } from '@app/iam/account';
import type {
  AuthenticatedAccount,
  IAccountLoader,
} from '@aurora/modules/authentication/domain/ports';
import { Injectable } from '@nestjs/common';

@Injectable()
export class IamAccountLoaderAdapter implements IAccountLoader {
  constructor(private readonly findAccount: IamFindAccountService) {}

  async loadById(accountId: string): Promise<AuthenticatedAccount | null> {
    const account = await this.findAccount.main({
      where: { id: accountId },
      include: [{ association: 'user' }, { association: 'roles' }, { association: 'tenants' }],
    });

    if (!account) return null;

    return mapToAuthenticatedAccount(account);
  }
}

function mapToAuthenticatedAccount(account: IamAccount): AuthenticatedAccount {
  const permissions = (account.dPermissions ?? {}) as Record<string, string[]>;
  const allPermissions = Array.isArray(permissions.all) ? permissions.all : [];

  return {
    id: account.id,
    clientId: account.clientId ?? null,
    email: account.email ?? null,
    username: account.username,
    isActive: account.isActive,
    scopes: (account.scopes ?? []) as string[],
    dApplicationCodes: (account.dApplicationCodes ?? []) as string[],
    dPermissions: { ...permissions, all: allPermissions },
    dTenants: account.dTenants ?? [],
    meta: (account.meta ?? null) as Record<string, unknown> | null,
    roles: (account.roles ?? []).map((role) => ({
      id: role.id,
      name: role.name,
      defaultRedirection: role.defaultRedirection ?? null,
      hasHiddenVerticalNavigation: role.hasHiddenVerticalNavigation ?? null,
    })),
    tenants: (account.tenants ?? []).map((tenant) => ({
      id: tenant.id,
      name: tenant.name,
    })),
    user: account.user
      ? {
          id: account.user.id,
          name: account.user.name,
          surname: account.user.surname ?? null,
          langId: account.user.langId ?? null,
          meta: (account.user.meta ?? null) as Record<string, unknown> | null,
        }
      : null,
    createdAt: serializeDate(account.createdAt),
    updatedAt: serializeDate(account.updatedAt),
    deletedAt: serializeDate(account.deletedAt),
  };
}

function serializeDate(value: unknown): string | null {
  if (value === null || value === undefined) return null;
  if (value instanceof Date) return value.toISOString();
  if (typeof value === 'string') return value;
  return null;
}
