import {
  ChangeDetectionStrategy,
  Component,
  DestroyRef,
  OnInit,
  inject,
  signal
} from '@angular/core';
import {
  FormBuilder,
  ReactiveFormsModule,
  Validators
} from '@angular/forms';
import { RouterLink } from '@angular/router';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import {
  catchError,
  finalize,
  of,
  Subject,
  switchMap
} from 'rxjs';
import { HttpErrorResponse } from '@angular/common/http';
import { ToastrService } from 'ngx-toastr';

import {
  CreateUserRequest,
  UserModalMode,
  UserStatus
} from '../../models/user.model';
import { UserService } from '../../data-access/user.service';

import { DataTable } from '../../../../shared/components/data-table/data-table';
import { DataTableQueryState } from '../../../../shared/components/data-table/data-table-query.state';
import {
  DataTableAction,
  DataTableActionEvent,
  DataTableColumn,
  DataTablePageEvent,
  DataTableQuery,
  DataTableRecord,
  DataTableSortEvent
} from '../../../../shared/components/data-table/data-table.types';
import { Modal } from '../../../../shared/components/modal/modal';

interface UserTableRow extends DataTableRecord {
  id: string;
  email: string;
  status: UserStatus;
  roles: string[];
  isActive: boolean;
  serialNumber: number;
  createdOn: string;
  lastLoginOn: string | null;
}

@Component({
  selector: 'app-users-list',
  standalone: true,
  imports: [
    ReactiveFormsModule,
    RouterLink,
    DataTable,
    Modal
  ],
  templateUrl: './users-list.html',
  styleUrl: './users-list.scss',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class UsersList implements OnInit {
  private readonly destroyRef = inject(DestroyRef);
  private readonly formBuilder = inject(FormBuilder);
  private readonly usersApi = inject(UserService);
  private readonly toastr = inject(ToastrService);

  private readonly usersQueryRequests$ =
    new Subject<DataTableQuery>();

  readonly users = signal<UserTableRow[]>([]);
  readonly queryState = new DataTableQueryState();

  readonly totalRecords = signal(0);
  readonly filteredRecords = signal(0);

  readonly userModalMode = signal<UserModalMode>('create');
  readonly showUserModal = signal(false);

  readonly saving = signal(false);
  readonly userSubmitAttempted = signal(false);

  readonly userForm = this.formBuilder.nonNullable.group({
    email: [
      '',
      [
        Validators.required,
        Validators.email,
        Validators.maxLength(256)
      ]
    ],
    password: [
      '',
      [
        Validators.required,
        Validators.minLength(8),
        Validators.maxLength(128)
      ]
    ],
    roleCodes: this.formBuilder.nonNullable.control<string[]>(
      [],
      [
        Validators.required,
        control =>
          control.value.length > 0
            ? null
            : { required: true }
      ]
    )
  });

  readonly columns = signal<DataTableColumn<UserTableRow>[]>([
    {
      key: 'serialNumber',
      label: 'Sr. No.',
      header: 'Sr. No.',
      sortable: false,
      width: '100px'
    },
    {
      key: 'email',
      label: 'Email',
      header: 'Email',
      sortable: true,
      align: 'left'
    },
    {
      key: 'status',
      label: 'User Status',
      header: 'User Status',
      sortable: true,
      formatter: value =>
        this.formatUserStatus(value as UserStatus)
    },
    {
      key: 'roles',
      label: 'Roles',
      header: 'Roles',
      sortable: false,
      align: 'left',
      formatter: value =>
        this.formatRoles(
          Array.isArray(value)
            ? value.filter(
                (item): item is string =>
                  typeof item === 'string'
              )
            : []
        )
    },
    {
      key: 'createdOn',
      label: 'Created On',
      header: 'Created On',
      sortable: true
    },
    {
      key: 'lastLoginOn',
      label: 'Last Login On',
      header: 'Last Login On',
      sortable: true,
      formatter: value =>
        typeof value === 'string' && value.trim().length > 0
          ? value
          : 'Never'
    },
    {
      key: 'isActive',
      label: 'Active',
      header: 'Active',
      sortable: true,
      badge: true,
      badgeClass: value =>
        value === true
          ? 'badge bg-success/10 text-success'
          : 'badge bg-danger/10 text-danger',
      formatter: value =>
        value === true ? 'Active' : 'Inactive'
    }
  ]);

   onActionClick(event: DataTableActionEvent<UserTableRow>): void {
      // const user = this.toUserDto(event.row);
  
      // switch (event.action.type) {
      //   case 'view':
      //     this.viewUser(user);
      //     break;
      //   case 'edit':
      //     this.openEditUser(user);
      //     break;
      //   case 'delete':
      //     this.requestDeleteUser(user);
      //     break;
      //   case 'activate':
      //     this.activateUser(user);
      //     break;
      // }
    }
  readonly actions = signal<DataTableAction<UserTableRow>[]>([]);

  constructor() {
    this.initializeUserQueryStream();
  }

  ngOnInit(): void {
    this.loadUsers(this.queryState.getQuery());
  }

  loadUsers(query: DataTableQuery): void {
    this.usersQueryRequests$.next(query);
  }

  onSearch(value: string): void {
    const query = this.queryState.setSearch(value.trim());
    this.loadUsers(query);
  }

  onPageChange(event: DataTablePageEvent): void {
    const query = this.queryState.setPage(event);
    this.loadUsers(query);
  }

  onSortChange(event: DataTableSortEvent): void {
    const query = this.queryState.setSort(event);
    this.loadUsers(query);
  }

  onRefresh(): void {
    const query = this.queryState.reset();
    this.loadUsers(query);
  }

  onAddUser(): void {
    this.userModalMode.set('create');
    this.resetUserForm();
    this.showUserModal.set(true);
  }

  closeUserModal(): void {
    if (this.saving()) {
      return;
    }

    this.showUserModal.set(false);
    this.resetUserForm();
  }

  saveUser(): void {
    this.userSubmitAttempted.set(true);

    if (this.userForm.invalid || this.saving()) {
      return;
    }

    const request = this.buildCreateUserRequest();

    if (request.roleCodes.length === 0) {
      this.userForm.controls.roleCodes.setErrors({
        required: true
      });

      return;
    }

    this.saving.set(true);

    this.usersApi
      .createUser(request)
      .pipe(
        finalize(() => {
          this.saving.set(false);
        }),
        takeUntilDestroyed(this.destroyRef)
      )
      .subscribe({
        next: () => {
          this.toastr.success('User created successfully.');

          this.showUserModal.set(false);
          this.resetUserForm();
          this.refreshUsersAfterSave();
        },
        error: (error: HttpErrorResponse) => {
          this.handleCreateUserError(error);
        }
      });
  }

  isControlInvalid(
    controlName: keyof typeof this.userForm.controls
  ): boolean {
    const control = this.userForm.controls[controlName];

    return this.userSubmitAttempted() && control.invalid;
  }

  hasControlError(
    controlName: keyof typeof this.userForm.controls,
    errorName: string
  ): boolean {
    const control = this.userForm.controls[controlName];

    return (
      this.userSubmitAttempted() &&
      control.hasError(errorName)
    );
  }

  private initializeUserQueryStream(): void {
    this.usersQueryRequests$
      .pipe(
        switchMap(requestedQuery =>
          this.usersApi.getDataTable(requestedQuery).pipe(
            catchError(() => {
              this.clearUsersGrid();
              this.toastr.error('Unable to load users.');

              return of(null);
            })
          )
        ),
        takeUntilDestroyed(this.destroyRef)
      )
      .subscribe(response => {
        if (response === null) {
          return;
        }

        const currentQuery = this.queryState.getQuery();

        if (response.draw !== currentQuery.draw) {
          return;
        }

        const usersWithSerialNumbers: UserTableRow[] =
          response.rows.map((user, index) => ({
            id: user.id,
            email: user.email,
            status: user.status,
            roles: [...user.roles],
            isActive: user.isActive,
            createdOn: user.createdOn,
            lastLoginOn: user.lastLoginOn,
            serialNumber:
              (currentQuery.page - 1) *
                currentQuery.pageSize +
              index +
              1
          }));

        this.users.set(usersWithSerialNumbers);
        this.totalRecords.set(response.total);
        this.filteredRecords.set(response.filtered);
      });
  }

  private buildCreateUserRequest(): CreateUserRequest {
    const value = this.userForm.getRawValue();

    const normalizedRoleCodes = Array.from(
      new Set(
        value.roleCodes
          .map(roleCode => roleCode.trim())
          .filter(roleCode => roleCode.length > 0)
      )
    );

    return {
      email: value.email.trim(),
      password: value.password,
      roleCodes: normalizedRoleCodes
    };
  }

  private refreshUsersAfterSave(): void {
    const query = this.queryState.reset();
    this.loadUsers(query);
  }

  private resetUserForm(): void {
    this.userForm.reset({
      email: '',
      password: '',
      roleCodes: []
    });

    this.userSubmitAttempted.set(false);

    this.userForm.markAsPristine();
    this.userForm.markAsUntouched();
    this.userForm.updateValueAndValidity({
      emitEvent: false
    });
  }

  private clearUsersGrid(): void {
    this.users.set([]);
    this.totalRecords.set(0);
    this.filteredRecords.set(0);
  }

  private handleCreateUserError(
    error: HttpErrorResponse
  ): void {
    if (error.status === 409) {
      this.toastr.error(
        'A user with this email address already exists.'
      );

      return;
    }

    if (error.status === 400) {
      this.toastr.error(
        this.extractApiErrorMessage(error) ??
          'The user information is invalid.'
      );

      return;
    }

    /*
     * Authentication/session errors should normally be handled
     * by the global authentication interceptor.
     */
    if (error.status === 401 || error.status === 403) {
      return;
    }

    this.toastr.error(
      'Unable to create the user. Please try again.'
    );
  }

  private extractApiErrorMessage(
    error: HttpErrorResponse
  ): string | null {
    const responseBody: unknown = error.error;

    if (
      typeof responseBody !== 'object' ||
      responseBody === null
    ) {
      return null;
    }

    const apiError = responseBody as {
      detail?: unknown;
      message?: unknown;
      title?: unknown;
    };

    if (typeof apiError.detail === 'string') {
      return apiError.detail;
    }

    if (typeof apiError.message === 'string') {
      return apiError.message;
    }

    if (typeof apiError.title === 'string') {
      return apiError.title;
    }

    return null;
  }

  private formatRoles(roles: readonly string[]): string {
    if (roles.length === 0) {
      return '—';
    }

    return roles
      .map(role => this.formatRoleCode(role))
      .join(', ');
  }

  private formatRoleCode(roleCode: string): string {
    return roleCode
      .split('_')
      .filter(part => part.length > 0)
      .map(
        part =>
          `${part.charAt(0).toUpperCase()}${part
            .slice(1)
            .toLowerCase()}`
      )
      .join(' ');
  }

  private formatUserStatus(status: UserStatus): string {
    switch (status) {
      case UserStatus.Pending:
        return 'Pending';

      case UserStatus.Active:
        return 'Active';

      case UserStatus.Suspended:
        return 'Suspended';

      case UserStatus.Locked:
        return 'Locked';

      case UserStatus.Disabled:
        return 'Disabled';

      default:
        return 'Unknown';
    }
  }
}
