import { CommonModule } from '@angular/common';
import { Component, OnInit, inject } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { CashlineApiService } from '../core/cashline-api.service';
import { CutoverI18nService } from '../core/cutover-i18n.service';
import { isAdministratorSession, isSystemOwnerSession } from '../core/uat-access.util';

interface AdminUserResponse {
  userId: number;
  loginName: string;
  displayName: string;
  userTypeCode: string;
  email: string | null;
  branchId: number | null;
  partyId: number | null;
  isActive: boolean;
  isLocked: boolean;
  roleCodes: string[];
  functionCodes: string[];
  lifecycleStatusCode: string;
  dateOfJoiningUtc: string;
  dateOfSuspensionUtc: string | null;
  lastAdminActionReason: string | null;
}

interface AdminRoleFunctionOptionResponse {
  roleCode: string;
  roleName: string;
  userTypeCode: string;
  functionSetCode: string;
  functionCodes: string[];
  functions: string[];
  functionItems: AdminFunctionGovernanceItemResponse[];
}

interface AdminFunctionConfigurationResponse {
  internalRoles: AdminRoleFunctionOptionResponse[];
  externalRoles: AdminRoleFunctionOptionResponse[];
  portalMenuSections: AdminPortalMenuSectionResponse[];
}

interface AdminPortalMenuSectionResponse {
  portalCode: string;
  userTypeCode: string;
  sectionCode: string;
  sectionLabel: string;
  displayOrder: number;
  isEnabled: boolean;
  functions: AdminFunctionGovernanceItemResponse[];
}

interface AdminFunctionGovernanceItemResponse {
  functionCode: string;
  functionLabel: string;
  menuSectionCode: string;
  menuSectionLabel: string;
  portalCode: string;
  userTypeCode: string;
  route: string;
  stepId: string;
  permissionCode: string;
  ruleCode: string;
  displayOrder: number;
  isEnabled: boolean;
}

interface BusinessPartyResponse {
  partyId: number;
  partyCode: string;
  partyName: string;
  partyType: string;
  email: string | null;
  mobileNumber: string | null;
}

interface AccountingOfficeClientAssignmentResponse {
  accountingOfficeUserId: number;
  partyId: number;
  partyCode: string;
  partyName: string;
  partyType: string;
  email: string | null;
  mobileNumber: string | null;
  assignedAtUtc: string;
  assignedByUserId: number | null;
}

type LifecycleAction = 'hold' | 'suspend' | 'reinstate' | 'revoke' | 'delete';

@Component({
  selector: 'app-user-administration-page',
  standalone: true,
  imports: [CommonModule, FormsModule],
  styleUrl: './user-administration-page.component.scss',
  template: `
    <section class="admin-console">
      <header class="admin-console__hero">
        <div>
          <span class="meta-label">{{ t('Administrator Console') }}</span>
          <h1>{{ t('User Administration') }}</h1>
        </div>
        <button type="button" class="admin-btn admin-btn--ghost" (click)="loadUsers()" [disabled]="loading">
          {{ loading ? t('Refreshing') : t('Refresh') }}
        </button>
      </header>

      <div class="admin-denied" *ngIf="!isAuthorized()">
        {{ t('Only Super Admin or System Owner users can manage system users.') }}
      </div>

      <ng-container *ngIf="isAuthorized()">
        <section class="admin-card admin-create">
          <div>
            <span class="meta-label">{{ t('Create new user') }}</span>
            <h2>{{ t('Provision account') }}</h2>
          </div>

          <form class="admin-create__grid" (ngSubmit)="createUser()">
            <label>
              <span>{{ t('Login name') }}</span>
              <input name="loginName" [(ngModel)]="createForm.loginName" required>
            </label>
            <label>
              <span>{{ t('Display name') }}</span>
              <input name="displayName" [(ngModel)]="createForm.displayName" required>
            </label>
            <label>
              <span>{{ t('Email') }}</span>
              <input name="email" [(ngModel)]="createForm.email">
            </label>
            <label>
              <span>{{ t('Password') }}</span>
              <input name="password" type="password" [(ngModel)]="createForm.password" required>
            </label>
            <label>
              <span>{{ t('User type') }}</span>
              <select name="userTypeCode" [(ngModel)]="createForm.userTypeCode" (ngModelChange)="onCreateUserTypeChanged()" [disabled]="isSystemOwner()">
                <option value="INTERNAL">{{ t('Internal') }}</option>
                <option value="EXTERNAL">{{ t('External') }}</option>
              </select>
            </label>
            <label>
              <span>{{ t(createForm.userTypeCode === 'EXTERNAL' ? 'External role' : 'Internal role') }}</span>
              <select name="primaryRoleCode" [(ngModel)]="primaryRoleCode" (ngModelChange)="onPrimaryRoleChanged()">
                <option value="">{{ t('Select role') }}</option>
                <option *ngFor="let role of createRoleOptions" [value]="role.roleCode">
                  {{ role.roleName }}
                </option>
              </select>
            </label>
            <label>
              <span>{{ t('Branch ID') }}</span>
              <input name="branchId" type="number" [(ngModel)]="createForm.branchId">
            </label>
            <label>
              <span>{{ t('Party ID') }}</span>
              <input name="partyId" type="number" [(ngModel)]="createForm.partyId">
            </label>

            <div class="admin-create__actions">
              <button type="submit" class="admin-btn admin-btn--primary" [disabled]="saving">
                {{ saving ? t('Creating') : t('Create user') }}
              </button>
            </div>
          </form>

          <div class="admin-function-config" *ngIf="isSystemOwner()">
            <span class="meta-label">{{ t('System Owner scope') }}</span>
            <h3>{{ t('Administrator and Accounting Office bootstrap governance') }}</h3>
            <div class="admin-role-list">
              <span>{{ t('Create Administrator users') }}</span>
              <span>{{ t('Govern Administrator users') }}</span>
              <span>{{ t('Create Accounting Office users') }}</span>
              <span>{{ t('Govern Accounting Office users') }}</span>
              <span>{{ t('Administrator and Accounting Office activity reports only') }}</span>
            </div>
          </div>

          <div class="admin-function-config" *ngIf="selectedCreateRole">
            <span class="meta-label">{{ t('Function configuration') }}</span>
            <h3>{{ selectedCreateRole.functionSetCode }}</h3>
            <div class="admin-function-grid">
              <label *ngFor="let option of selectedCreateFunctionOptions">
                <input
                  type="checkbox"
                  [checked]="createFunctionCodes.includes(option.code)"
                  (change)="toggleCreateFunction(option.code, $event)"
                >
                <span>{{ t(option.label) }}</span>
              </label>
            </div>
          </div>
        </section>

        <section class="admin-card admin-managed-users">
          <div class="admin-card__toolbar">
            <div>
              <span class="meta-label">{{ t('Managed users') }}</span>
              <h2>{{ users.length }} {{ t('accounts') }}</h2>
            </div>
            <label class="admin-filter">
              <span>{{ t('User type') }}</span>
              <select [(ngModel)]="userTypeFilter" (ngModelChange)="loadUsers()">
                <option value="">{{ t('All') }}</option>
                <option value="INTERNAL">{{ t('Internal') }}</option>
                <option value="EXTERNAL">{{ t('External') }}</option>
              </select>
            </label>
            <div class="admin-actions">
              <button type="button" class="admin-btn admin-btn--ghost" (click)="exportReport('PDF')">{{ t('Export PDF') }}</button>
              <button type="button" class="admin-btn admin-btn--ghost" (click)="exportReport('EXCEL')">{{ t('Export Excel') }}</button>
              <button type="button" class="admin-btn admin-btn--ghost" (click)="printReport()">{{ t('Print') }}</button>
            </div>
          </div>

          <p class="admin-message admin-message--error" *ngIf="errorText">{{ t(errorText) }}</p>
          <p class="admin-message admin-message--success" *ngIf="successText">{{ t(successText) }}</p>

          <div class="admin-table-wrap">
            <table class="admin-table">
              <thead>
                <tr>
                  <th>{{ t('User') }}</th>
                  <th>{{ t('Type') }}</th>
                  <th>{{ t('Roles') }}</th>
                  <th>{{ t('Status') }}</th>
                  <th>{{ t('Dates and reason') }}</th>
                  <th>{{ t('Actions') }}</th>
                </tr>
              </thead>
              <tbody>
                <tr *ngFor="let user of users">
                  <td>
                    <strong>{{ user.displayName }}</strong>
                    <span>{{ user.loginName }}</span>
                    <small *ngIf="user.email">{{ user.email }}</small>
                  </td>
                  <td>{{ user.userTypeCode }}</td>
                  <td>
                    <div class="admin-role-list">
                      <span *ngFor="let role of user.roleCodes">{{ role }}</span>
                    </div>
                    <div class="admin-role-list admin-role-list--muted" *ngIf="user.functionCodes.length">
                      <span *ngFor="let fn of user.functionCodes">{{ functionLabel(fn) }}</span>
                    </div>
                  </td>
                  <td>
                    <span class="admin-status" [attr.data-status]="user.lifecycleStatusCode">{{ user.lifecycleStatusCode }}</span>
                  </td>
                  <td>
                    <span>{{ t('Joined') }}: {{ user.dateOfJoiningUtc | date: 'yyyy-MM-dd' }}</span>
                    <span *ngIf="user.dateOfSuspensionUtc">{{ t('Suspended') }}: {{ user.dateOfSuspensionUtc | date: 'yyyy-MM-dd' }}</span>
                    <small *ngIf="user.lastAdminActionReason">{{ user.lastAdminActionReason }}</small>
                  </td>
                  <td>
                    <div class="admin-actions">
                      <button type="button" class="admin-btn admin-btn--ghost" *ngIf="!isSystemOwner()" (click)="editRoles(user)">{{ t('Roles') }}</button>
                      <button type="button" class="admin-btn admin-btn--ghost" *ngIf="!isSystemOwner()" (click)="editFunctions(user)" [disabled]="!user.roleCodes.length">{{ t('Functions') }}</button>
                      <button type="button" class="admin-btn admin-btn--ghost" *ngIf="isAccountingOfficeUser(user)" (click)="manageAccountingOfficeClients(user)">{{ t('Clients') }}</button>
                      <button type="button" class="admin-btn admin-btn--ghost" *ngIf="!isSystemOwner()" (click)="runLifecycle(user, 'hold')" [disabled]="user.isLocked">{{ t('Hold') }}</button>
                      <button type="button" class="admin-btn admin-btn--ghost" *ngIf="!isSystemOwner()" (click)="runLifecycle(user, 'reinstate')" [disabled]="!user.isLocked && user.isActive">{{ t('Reinstate') }}</button>
                      <button type="button" class="admin-btn admin-btn--warning" *ngIf="!isSystemOwner()" (click)="runLifecycle(user, 'suspend')" [disabled]="!user.isActive">{{ t('Suspend') }}</button>
                      <button type="button" class="admin-btn admin-btn--warning" *ngIf="!isSystemOwner()" (click)="runLifecycle(user, 'revoke')" [disabled]="!user.roleCodes.length">{{ t('Revoke') }}</button>
                      <button type="button" class="admin-btn admin-btn--danger" *ngIf="!isSystemOwner()" (click)="runLifecycle(user, 'delete')" [disabled]="!user.isActive && !user.roleCodes.length">{{ t('Delete') }}</button>
                    </div>
                  </td>
                </tr>
              </tbody>
            </table>
          </div>
        </section>

        <section class="admin-card admin-managed-users" *ngIf="selectedAccountingOfficeUser">
          <div class="admin-card__toolbar">
            <div>
              <span class="meta-label">{{ t('Accounting Office client scope') }}</span>
              <h2>{{ selectedAccountingOfficeUser.displayName }}</h2>
            </div>
            <label class="admin-filter">
              <span>{{ t('Client party') }}</span>
              <select [(ngModel)]="selectedAccountingOfficePartyId">
                <option [ngValue]="null">{{ t('Select client party') }}</option>
                <option *ngFor="let party of availableClientParties" [ngValue]="party.partyId">
                  {{ party.partyCode }} · {{ party.partyName }} · {{ party.partyType }}
                </option>
              </select>
            </label>
            <div class="admin-actions">
              <button type="button" class="admin-btn admin-btn--primary" (click)="assignAccountingOfficeClient()" [disabled]="!selectedAccountingOfficePartyId">
                {{ t('Assign client') }}
              </button>
            </div>
          </div>

          <div class="admin-role-list" *ngIf="selectedAccountingOfficeAssignments.length; else noAccountingOfficeAssignments">
            <span *ngFor="let assignment of selectedAccountingOfficeAssignments">
              {{ assignment.partyCode }} · {{ assignment.partyName }} · {{ assignment.partyType }}
              <button type="button" class="admin-inline-action" (click)="removeAccountingOfficeClient(assignment)">
                {{ t('Remove') }}
              </button>
            </span>
          </div>

          <ng-template #noAccountingOfficeAssignments>
            <p class="admin-message">{{ t('No client parties are assigned to this Accounting Office user yet.') }}</p>
          </ng-template>
        </section>

        <section class="admin-card admin-roles admin-role-catalog">
          <span class="meta-label">{{ t('Available role and function sets') }}</span>
          <div class="admin-role-list">
            <span *ngFor="let role of allRoleOptions">{{ role.roleCode }} · {{ role.functionSetCode }}</span>
          </div>
        </section>

        <section class="admin-card admin-roles admin-governance-card" *ngIf="!isSystemOwner()">
          <span class="meta-label">{{ t('Portal menu governance') }}</span>
          <div class="admin-governance-grid">
            <article *ngFor="let section of visiblePortalMenuSections">
              <div class="admin-governance-head">
                <div>
                  <strong>{{ t(section.sectionLabel) }}</strong>
                  <small>{{ section.userTypeCode }} · {{ section.portalCode }} · {{ section.sectionCode }}</small>
                </div>
                <button
                  type="button"
                  class="admin-btn admin-btn--ghost"
                  (click)="toggleSection(section)"
                >
                  {{ section.isEnabled ? t('Disable section') : t('Enable section') }}
                </button>
              </div>
              <div class="admin-role-list admin-role-list--muted">
                <span *ngFor="let fn of section.functions">
                  {{ t(fn.functionLabel) }} · {{ fn.permissionCode }}
                  <button
                    type="button"
                    class="admin-inline-action"
                    (click)="toggleFunction(section, fn)"
                  >
                    {{ fn.isEnabled ? t('Disable') : t('Enable') }}
                  </button>
                </span>
              </div>
            </article>
          </div>
        </section>
      </ng-container>
    </section>
  `
})
export class UserAdministrationPageComponent implements OnInit {
  private readonly api = inject(CashlineApiService);
  private readonly i18n = inject(CutoverI18nService);

  protected users: AdminUserResponse[] = [];
  protected functionConfiguration: AdminFunctionConfigurationResponse | null = null;
  protected availableClientParties: BusinessPartyResponse[] = [];
  protected selectedAccountingOfficeAssignments: AccountingOfficeClientAssignmentResponse[] = [];
  protected selectedAccountingOfficeUser: AdminUserResponse | null = null;
  protected selectedAccountingOfficePartyId: number | null = null;
  protected loading = false;
  protected saving = false;
  protected errorText = '';
  protected successText = '';
  protected userTypeFilter = '';
  protected createForm = {
    loginName: '',
    displayName: '',
    email: '',
    password: '',
    userTypeCode: 'INTERNAL',
    branchId: 1 as number | null,
    partyId: null as number | null
  };
  protected primaryRoleCode = 'ADMIN';
  protected createFunctionCodes: string[] = [];

  async ngOnInit(): Promise<void> {
    if (!this.isAuthorized()) {
      return;
    }

    await Promise.all([this.loadUsers(), this.loadFunctionConfiguration()]);
  }

  protected isAuthorized(): boolean {
    return this.api.currentSession ? isAdministratorSession(this.api.currentSession) || isSystemOwnerSession(this.api.currentSession) : false;
  }

  protected isSystemOwner(): boolean {
    return this.api.currentSession ? isSystemOwnerSession(this.api.currentSession) : false;
  }

  protected t(value: string): string {
    return this.i18n.text(value);
  }

  protected async loadUsers(): Promise<void> {
    this.loading = true;
    this.clearMessages();
    try {
      const query = this.userTypeFilter ? `?userTypeCode=${encodeURIComponent(this.userTypeFilter)}` : '';
      this.users = await this.api.get<AdminUserResponse[]>(`/api/v1/iam/users${query}`);
    } catch {
      this.errorText = 'Unable to load users.';
    } finally {
      this.loading = false;
    }
  }

  protected get createRoleOptions(): AdminRoleFunctionOptionResponse[] {
    return this.createForm.userTypeCode === 'EXTERNAL'
      ? this.functionConfiguration?.externalRoles ?? []
      : this.functionConfiguration?.internalRoles.filter((role) => this.isSystemOwner()
        ? ['SUPER_ADMIN', 'ACCOUNTING_OFFICE_USER', 'ACCOUNTING_OFFICE_SUPERVISOR'].includes(role.roleCode)
        : role.roleCode !== 'SUPER_ADMIN' && role.roleCode !== 'SYSTEM_OWNER' && role.roleCode !== 'ACCOUNTING_OFFICE_USER' && role.roleCode !== 'ACCOUNTING_OFFICE_SUPERVISOR') ?? [];
  }

  protected get selectedCreateRole(): AdminRoleFunctionOptionResponse | null {
    return this.createRoleOptions.find((role) => role.roleCode === this.primaryRoleCode) ?? null;
  }

  protected get selectedCreateFunctionOptions(): { code: string; label: string }[] {
    const role = this.selectedCreateRole;
    if (!role) {
      return [];
    }

    return role.functionCodes.map((code, index) => ({
      code,
      label: role.functions[index] ?? code
    }));
  }

  protected get allRoleOptions(): AdminRoleFunctionOptionResponse[] {
    if (this.isSystemOwner()) {
      return (this.functionConfiguration?.internalRoles ?? []).filter((role) =>
        ['SUPER_ADMIN', 'ACCOUNTING_OFFICE_USER', 'ACCOUNTING_OFFICE_SUPERVISOR'].includes(role.roleCode));
    }

    return [
      ...(this.functionConfiguration?.internalRoles ?? []),
      ...(this.functionConfiguration?.externalRoles ?? [])
    ];
  }

  protected get visiblePortalMenuSections(): AdminPortalMenuSectionResponse[] {
    const sections = this.functionConfiguration?.portalMenuSections ?? [];
    return this.isSystemOwner()
      ? sections.filter((section) =>
        section.sectionCode.includes('ADMINISTRATOR')
        || section.sectionCode.includes('USER_')
        || section.sectionCode.includes('ACCOUNTING_OFFICE')
        || section.sectionCode.includes('GOVERNMENT_SUBMISSIONS')
        || section.sectionCode.includes('PAYMENTS_SETTLEMENT')
        || section.sectionCode.includes('REPORTS_STATEMENTS')
        || section.sectionCode.includes('ONBOARDING_KYC'))
      : sections;
  }

  protected async loadFunctionConfiguration(): Promise<void> {
    try {
      this.functionConfiguration = await this.api.get<AdminFunctionConfigurationResponse>('/api/v1/iam/function-configuration');
      this.onCreateUserTypeChanged();
    } catch {
      this.errorText = 'Unable to load function configuration.';
    }
  }

  protected async toggleSection(section: AdminPortalMenuSectionResponse): Promise<void> {
    await this.updateFunctionGovernance({
      portalCode: section.portalCode,
      userTypeCode: section.userTypeCode,
      sectionCode: section.sectionCode,
      sectionLabel: section.sectionLabel,
      sectionEnabled: !section.isEnabled,
      functionCode: null,
      functionLabel: null,
      functionEnabled: null,
      displayOrder: section.displayOrder
    });
  }

  protected async toggleFunction(section: AdminPortalMenuSectionResponse, fn: AdminFunctionGovernanceItemResponse): Promise<void> {
    await this.updateFunctionGovernance({
      portalCode: section.portalCode,
      userTypeCode: section.userTypeCode,
      sectionCode: section.sectionCode,
      sectionLabel: section.sectionLabel,
      sectionEnabled: section.isEnabled,
      functionCode: fn.functionCode,
      functionLabel: fn.functionLabel,
      functionEnabled: !fn.isEnabled,
      displayOrder: fn.displayOrder
    });
  }

  protected onCreateUserTypeChanged(): void {
    const firstRole = this.createRoleOptions[0];
    this.primaryRoleCode = firstRole?.roleCode ?? '';
    this.createForm.userTypeCode = this.isSystemOwner() ? 'INTERNAL' : this.createForm.userTypeCode;
    this.createForm.branchId = this.createForm.userTypeCode === 'INTERNAL' ? 1 : null;
    this.resetCreateFunctionCodes();
  }

  protected onPrimaryRoleChanged(): void {
    this.clearMessages();
    this.resetCreateFunctionCodes();
  }

  protected toggleCreateFunction(functionCode: string, event: Event): void {
    const checked = (event.target as HTMLInputElement).checked;
    this.createFunctionCodes = checked
      ? [...new Set([...this.createFunctionCodes, functionCode])]
      : this.createFunctionCodes.filter((code) => code !== functionCode);
  }

  protected async createUser(): Promise<void> {
    this.saving = true;
    this.clearMessages();
    try {
      await this.api.post('/api/v1/iam/users', {
        loginName: this.createForm.loginName,
        displayName: this.createForm.displayName,
        userTypeCode: this.createForm.userTypeCode,
        password: this.createForm.password,
        email: this.createForm.email || null,
        branchId: this.createForm.branchId || null,
        partyId: this.createForm.partyId || null,
        roleCodes: this.primaryRoleCode ? [this.primaryRoleCode] : [],
        functionCodes: this.createFunctionCodes
      });
      this.successText = 'User created successfully.';
      this.createForm.loginName = '';
      this.createForm.displayName = '';
      this.createForm.email = '';
      this.createForm.password = '';
      await this.loadUsers();
    } catch (error) {
      this.errorText = this.extractError(error, 'Unable to create user.');
    } finally {
      this.saving = false;
    }
  }

  protected async editRoles(user: AdminUserResponse): Promise<void> {
    const validRoles = this.allRoleOptions
      .filter((role) => role.userTypeCode === user.userTypeCode)
      .map((role) => role.roleCode)
      .join(', ');
    const nextRoles = window.prompt(`Role codes, separated by commas. Valid options: ${validRoles}`, user.roleCodes.join(', '));
    if (nextRoles === null) {
      return;
    }

    this.clearMessages();
    try {
      const roleCodes = this.parseRoles(nextRoles);
      const validFunctionCodes = this.resolveFunctionCodesForRoles(roleCodes);
      await this.api.put(`/api/v1/iam/users/${user.userId}/roles`, {
        roleCodes,
        functionCodes: user.functionCodes.filter((code) => validFunctionCodes.includes(code))
      });
      this.successText = 'User roles updated.';
      await this.loadUsers();
    } catch (error) {
      this.errorText = this.extractError(error, 'Unable to update roles.');
    }
  }

  protected async editFunctions(user: AdminUserResponse): Promise<void> {
    const validFunctionCodes = this.resolveFunctionCodesForRoles(user.roleCodes);
    const validLabels = validFunctionCodes
      .map((code) => `${code} (${this.functionLabel(code)})`)
      .join(', ');
    const nextFunctions = window.prompt(
      `Function codes, separated by commas. Valid options: ${validLabels}`,
      user.functionCodes.join(', ')
    );
    if (nextFunctions === null) {
      return;
    }

    this.clearMessages();
    try {
      await this.api.put(`/api/v1/iam/users/${user.userId}/roles`, {
        roleCodes: user.roleCodes,
        functionCodes: this.parseRoles(nextFunctions)
      });
      this.successText = 'User functions updated.';
      await this.loadUsers();
    } catch (error) {
      this.errorText = this.extractError(error, 'Unable to update functions.');
    }
  }

  protected async manageAccountingOfficeClients(user: AdminUserResponse): Promise<void> {
    this.clearMessages();
    this.selectedAccountingOfficeUser = user;
    this.selectedAccountingOfficePartyId = null;
    try {
      if (!this.availableClientParties.length) {
        const parties = await this.api.get<BusinessPartyResponse[]>('/api/v1/iam/parties');
        this.availableClientParties = parties.filter((party) => party.partyType === 'SUPPLIER' || party.partyType === 'CUSTOMER');
      }

      this.selectedAccountingOfficeAssignments = await this.api.get<AccountingOfficeClientAssignmentResponse[]>(
        `/api/v1/iam/accounting-office-users/${user.userId}/client-assignments`);
    } catch (error) {
      this.errorText = this.extractError(error, 'Unable to load Accounting Office client assignments.');
    }
  }

  protected async assignAccountingOfficeClient(): Promise<void> {
    if (!this.selectedAccountingOfficeUser || !this.selectedAccountingOfficePartyId) {
      return;
    }

    this.clearMessages();
    try {
      await this.api.post(
        `/api/v1/iam/accounting-office-users/${this.selectedAccountingOfficeUser.userId}/client-assignments`,
        { partyId: this.selectedAccountingOfficePartyId });
      this.successText = 'Accounting Office client assigned.';
      await this.manageAccountingOfficeClients(this.selectedAccountingOfficeUser);
    } catch (error) {
      this.errorText = this.extractError(error, 'Unable to assign Accounting Office client.');
    }
  }

  protected async removeAccountingOfficeClient(assignment: AccountingOfficeClientAssignmentResponse): Promise<void> {
    if (!this.selectedAccountingOfficeUser) {
      return;
    }

    this.clearMessages();
    try {
      await this.api.delete(
        `/api/v1/iam/accounting-office-users/${this.selectedAccountingOfficeUser.userId}/client-assignments/${assignment.partyId}`,
        {});
      this.successText = 'Accounting Office client removed.';
      await this.manageAccountingOfficeClients(this.selectedAccountingOfficeUser);
    } catch (error) {
      this.errorText = this.extractError(error, 'Unable to remove Accounting Office client.');
    }
  }

  protected async runLifecycle(user: AdminUserResponse, action: LifecycleAction): Promise<void> {
    const reason = window.prompt(`Reason for ${action}`, '');
    if (!reason?.trim()) {
      this.errorText = 'A reason is required before this action can be completed.';
      return;
    }

    if (action === 'delete' && !window.confirm(`Delete user ${user.loginName}? This is a soft delete that disables access.`)) {
      return;
    }

    this.clearMessages();
    try {
      const body = { reason };
      if (action === 'delete') {
        await this.api.delete(`/api/v1/iam/users/${user.userId}`, body);
      } else {
        await this.api.post(`/api/v1/iam/users/${user.userId}/${action}`, body);
      }

      this.successText = `User ${action} completed.`;
      await this.loadUsers();
    } catch (error) {
      this.errorText = this.extractError(error, `Unable to ${action} user.`);
    }
  }

  protected async exportReport(format: 'PDF' | 'EXCEL'): Promise<void> {
    const query = new URLSearchParams({ format });
    if (this.userTypeFilter) {
      query.set('userTypeCode', this.userTypeFilter);
    }

    const file = await this.api.download(`/api/v1/iam/reports/users/export?${query.toString()}`);
    const url = window.URL.createObjectURL(file.blob);
    const link = document.createElement('a');
    link.href = url;
    link.download = file.fileName ?? (format === 'PDF' ? 'cashline-user-administration-report.pdf' : 'cashline-user-administration-report.xls');
    link.click();
    window.URL.revokeObjectURL(url);
  }

  protected printReport(): void {
    const printableRows = this.users.map((user) => `
      <tr>
        <td>${this.escapeHtml(user.displayName)}</td>
        <td>${this.escapeHtml(user.userTypeCode)}</td>
        <td>${this.escapeHtml(user.roleCodes.join(', ') || 'NO_ACTIVE_ROLE')}</td>
        <td>${this.escapeHtml(user.dateOfJoiningUtc?.slice(0, 10) ?? '')}</td>
        <td>${this.escapeHtml(user.dateOfSuspensionUtc?.slice(0, 10) ?? '')}</td>
        <td>${this.escapeHtml(user.lastAdminActionReason ?? '')}</td>
      </tr>`).join('');
    const printWindow = window.open('', '_blank', 'width=1000,height=700');
    if (!printWindow) {
      return;
    }

    printWindow.document.write(`
      <html><head><title>User Administration Report</title></head>
      <body>
        <h1>User Administration Report</h1>
        <table border="1" cellspacing="0" cellpadding="6">
          <thead><tr><th>User Name</th><th>User Type</th><th>Role</th><th>Date of Joining</th><th>Date of Suspension</th><th>Reason</th></tr></thead>
          <tbody>${printableRows}</tbody>
        </table>
      </body></html>`);
    printWindow.document.close();
    printWindow.print();
  }

  private parseRoles(value: string): string[] {
    return value
      .split(',')
      .map((role) => role.trim().toUpperCase())
      .filter(Boolean);
  }

  private resetCreateFunctionCodes(): void {
    this.createFunctionCodes = this.selectedCreateRole?.functionCodes.slice() ?? [];
  }

  protected functionLabel(functionCode: string): string {
    for (const role of this.allRoleOptions) {
      const index = role.functionCodes.indexOf(functionCode);
      if (index >= 0) {
        return this.t(role.functions[index] ?? functionCode);
      }
    }

    return functionCode;
  }

  private resolveFunctionCodesForRoles(roleCodes: string[]): string[] {
    return this.allRoleOptions
      .filter((role) => roleCodes.includes(role.roleCode))
      .flatMap((role) => role.functionCodes);
  }

  private clearMessages(): void {
    this.errorText = '';
    this.successText = '';
  }

  protected isAccountingOfficeUser(user: AdminUserResponse): boolean {
    return user.roleCodes.includes('ACCOUNTING_OFFICE_USER') || user.roleCodes.includes('ACCOUNTING_OFFICE_SUPERVISOR');
  }

  private async updateFunctionGovernance(payload: {
    portalCode: string;
    userTypeCode: string;
    sectionCode: string;
    sectionLabel: string;
    sectionEnabled: boolean;
    functionCode: string | null;
    functionLabel: string | null;
    functionEnabled: boolean | null;
    displayOrder: number;
  }): Promise<void> {
    this.clearMessages();
    try {
      this.functionConfiguration = await this.api.put<AdminFunctionConfigurationResponse>('/api/v1/iam/function-governance', payload);
      this.resetCreateFunctionCodes();
      this.successText = 'Function governance updated.';
      await this.loadUsers();
    } catch (error) {
      this.errorText = this.extractError(error, 'Unable to update function governance.');
    }
  }

  private extractError(error: unknown, fallback: string): string {
    const response = error as { error?: { message?: string; title?: string } };
    return response.error?.message ?? response.error?.title ?? fallback;
  }

  private escapeHtml(value: string): string {
    return value
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&#39;');
  }
}
