import { Injectable, inject, signal, computed } from '@angular/core';
import { Apollo } from 'apollo-angular';
import { Router } from '@angular/router';
import { gql } from 'apollo-angular';
import { map, tap, catchError, of } from 'rxjs';

const LOGIN_MUTATION = gql`
  mutation Login($input: LoginRequestInput!) {
    login(input: $input) {
      success
      token
      error
      user {
        id
        username
        role
        studentId
        studentName
      }
    }
  }
`;

const REGISTER_MUTATION = gql`
  mutation Register($input: RegisterRequestInput!) {
    register(input: $input) {
      success
      token
      error
      recoveryCode
      user {
        id
        username
        role
        studentId
        studentName
      }
    }
  }
`;

const RESET_PASSWORD_MUTATION = gql`
  mutation ResetPassword($input: ResetPasswordRequestInput!) {
    resetPassword(input: $input) {
      success
      error
    }
  }
`;

const ME_QUERY = gql`
  query Me {
    me {
      id
      username
      role
      studentId
      studentName
    }
  }
`;

export interface UserInfo {
  id: number;
  username: string;
  role: string;
  studentId: number | null;
  studentName: string | null;
}

export interface AuthResponse {
  success: boolean;
  token?: string;
  user?: UserInfo;
  error?: string;
  recoveryCode?: string;
}

export interface ResetPasswordResponse {
  success: boolean;
  error?: string;
}

const TOKEN_KEY = 'auth_token';
const USER_KEY = 'auth_user';

@Injectable({ providedIn: 'root' })
export class AuthService {
  private apollo = inject(Apollo);
  private router = inject(Router);

  private _user = signal<UserInfo | null>(null);
  private _isAuthenticated = signal(false);
  private _isLoading = signal(true);

  readonly user = this._user.asReadonly();
  readonly isAuthenticated = this._isAuthenticated.asReadonly();
  readonly isLoading = this._isLoading.asReadonly();
  readonly isAdmin = computed(() => this._user()?.role === 'Admin');
  readonly studentId = computed(() => this._user()?.studentId ?? null);

  constructor() {
    this.loadStoredAuth();
  }

  private loadStoredAuth(): void {
    const token = localStorage.getItem(TOKEN_KEY);
    const userJson = localStorage.getItem(USER_KEY);

    if (token && userJson) {
      try {
        const user = JSON.parse(userJson) as UserInfo;
        this._user.set(user);
        this._isAuthenticated.set(true);
      } catch {
        this.clearAuth();
      }
    }
    this._isLoading.set(false);
  }

  login(username: string, password: string) {
    return this.apollo
      .mutate<{ login: AuthResponse }>({
        mutation: LOGIN_MUTATION,
        variables: { input: { username, password } },
      })
      .pipe(
        map((result) => result.data?.login ?? { success: false, error: 'Error de conexion' }),
        tap((response) => {
          if (response.success && response.token && response.user) {
            this.setAuth(response.token, response.user);
          }
        })
      );
  }

  register(username: string, password: string, name?: string, email?: string) {
    return this.apollo
      .mutate<{ register: AuthResponse }>({
        mutation: REGISTER_MUTATION,
        variables: { input: { username, password, name, email } },
      })
      .pipe(
        map((result) => result.data?.register ?? { success: false, error: 'Error de conexion' }),
        tap((response) => {
          if (response.success && response.token && response.user) {
            this.setAuth(response.token, response.user);
          }
        })
      );
  }

  logout(): void {
    this.clearAuth();
    this.router.navigate(['/login']);
  }

  resetPassword(username: string, recoveryCode: string, newPassword: string) {
    return this.apollo
      .mutate<{ resetPassword: ResetPasswordResponse }>({
        mutation: RESET_PASSWORD_MUTATION,
        variables: { input: { username, recoveryCode, newPassword } },
      })
      .pipe(
        map((result) => result.data?.resetPassword ?? { success: false, error: 'Error de conexion' })
      );
  }

  getToken(): string | null {
    return localStorage.getItem(TOKEN_KEY);
  }

  canModifyStudent(studentId: number): boolean {
    const user = this._user();
    if (!user) return false;
    if (user.role === 'Admin') return true;
    return user.studentId === studentId;
  }

  private setAuth(token: string, user: UserInfo): void {
    localStorage.setItem(TOKEN_KEY, token);
    localStorage.setItem(USER_KEY, JSON.stringify(user));
    this._user.set(user);
    this._isAuthenticated.set(true);
  }

  private clearAuth(): void {
    localStorage.removeItem(TOKEN_KEY);
    localStorage.removeItem(USER_KEY);
    this._user.set(null);
    this._isAuthenticated.set(false);
  }
}
