import { Injectable, inject, signal, computed } from '@angular/core';
import { Router } from '@angular/router';
import { Observable, tap, BehaviorSubject } from 'rxjs';
import { ApiService } from './api.service';
import { StorageService } from './storage.service';
import {
  LoginDto,
  LoginResponseDto,
  DigiLockerLoginDto,
  DigiLockerLoginResponseDto,
  CurrentUserDto,
  DepartmentResponseDto,
} from '../../api/models';

export type UserType = 'APPLICANT' | 'DEPARTMENT' | 'ADMIN';

@Injectable({
  providedIn: 'root',
})
export class AuthService {
  private readonly api = inject(ApiService);
  private readonly storage = inject(StorageService);
  private readonly router = inject(Router);

  private readonly currentUserSubject = new BehaviorSubject<CurrentUserDto | null>(null);
  readonly currentUser$ = this.currentUserSubject.asObservable();

  private readonly _currentUser = signal<CurrentUserDto | null>(null);
  readonly currentUser = this._currentUser.asReadonly();

  private readonly _isAuthenticated = signal(false);
  readonly isAuthenticated = this._isAuthenticated.asReadonly();

  private readonly _userType = signal<UserType | null>(null);
  readonly userType = this._userType.asReadonly();

  readonly isDepartment = computed(() => this._userType() === 'DEPARTMENT');
  readonly isApplicant = computed(() => this._userType() === 'APPLICANT');
  readonly isAdmin = computed(() => this._userType() === 'ADMIN');

  constructor() {
    this.loadStoredUser();
  }

  private loadStoredUser(): void {
    const token = this.storage.getToken();
    const user = this.storage.getUser<CurrentUserDto>();

    if (token && user) {
      this.currentUserSubject.next(user);
      this._currentUser.set(user);
      this._isAuthenticated.set(true);
      this._userType.set(user.type);
    }
  }

  async login(email: string, password: string): Promise<void> {
    const response = await this.api.postRaw<any>('/auth/login', { email, password }).toPromise();

    if (!response) {
      throw new Error('Login failed');
    }

    this.storage.setToken(response.accessToken);

    const userType: UserType =
      response.user.role === 'ADMIN' ? 'ADMIN' :
      response.user.role === 'DEPARTMENT' ? 'DEPARTMENT' : 'APPLICANT';

    const user: CurrentUserDto = {
      id: response.user.id,
      type: userType,
      name: response.user.name,
      email: response.user.email,
      departmentId: response.user.departmentId,
      walletAddress: response.user.walletAddress,
    };

    this.storage.setUser(user);
    this.currentUserSubject.next(user);
    this._currentUser.set(user);
    this._isAuthenticated.set(true);
    this._userType.set(userType);
  }

  departmentLogin(dto: LoginDto): Observable<LoginResponseDto> {
    return this.api.postRaw<LoginResponseDto>('/auth/department/login', dto).pipe(
      tap((response) => {
        this.storage.setToken(response.accessToken);
        const user: CurrentUserDto = {
          id: response.department.id,
          type: 'DEPARTMENT',
          name: response.department.name,
          email: response.department.contactEmail || '',
          departmentCode: response.department.code,
        };
        this.storage.setUser(user);
        this.currentUserSubject.next(user);
        this._currentUser.set(user);
        this._isAuthenticated.set(true);
        this._userType.set('DEPARTMENT');
      })
    );
  }

  digiLockerLogin(dto: DigiLockerLoginDto): Observable<DigiLockerLoginResponseDto> {
    return this.api.postRaw<DigiLockerLoginResponseDto>('/auth/digilocker/login', dto).pipe(
      tap((response) => {
        this.storage.setToken(response.accessToken);
        const user: CurrentUserDto = {
          id: response.applicant.id,
          type: 'APPLICANT',
          name: response.applicant.name,
          email: response.applicant.email || '',
          digilockerId: response.applicant.digilockerId,
        };
        this.storage.setUser(user);
        this.currentUserSubject.next(user);
        this._currentUser.set(user);
        this._isAuthenticated.set(true);
        this._userType.set('APPLICANT');
      })
    );
  }

  logout(): void {
    this.storage.clear();
    this.currentUserSubject.next(null);
    this._currentUser.set(null);
    this._isAuthenticated.set(false);
    this._userType.set(null);
    this.router.navigate(['/login']);
  }

  getCurrentUser(): CurrentUserDto | null {
    return this.currentUserSubject.value;
  }

  getToken(): string | null {
    return this.storage.getToken();
  }

  hasRole(role: UserType): boolean {
    return this._userType() === role;
  }

  hasAnyRole(roles: UserType[]): boolean {
    const currentType = this._userType();
    return currentType !== null && roles.includes(currentType);
  }
}
