import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { BehaviorSubject, Observable, tap } from 'rxjs';
import { Router, ActivatedRoute } from '@angular/router';
import { LoginRequest } from '../model/auth/LoginRequest';
import { RegisterRequest } from '../model/auth/RegisterRequest';
import { AuthResponse } from '../model/auth/AuthResponse';
import { User } from '../model/User';
import { environment } from '@environments/environment';

const TOKEN_KEY = 'token';
const USER_KEY = 'user';

@Injectable({
  providedIn: 'root',
})
export class AuthService {
  private authUrl = `${environment.apiUrl}/auth`;
  private userUrl = `${environment.apiUrl}/users`;
  private oauthUrl = `${environment.apiUrl}/oauth2`;

  private http = inject(HttpClient);
  private router = inject(Router);
  private route = inject(ActivatedRoute);

  userSubject: BehaviorSubject<User | null>;

  constructor() {
    this.userSubject = new BehaviorSubject<User | null>(this.getUserFromStorage());

    // Check for token in URL (OAuth callback) on initialization
    this.route.queryParams.subscribe((params) => {
      const token = params['token'];
      if (token) {
        this.handleOAuthCallback(token);
      }
    });

    if (this.getToken()) {
      this.loadCurrentUser();
    }
  }

  private handleOAuthCallback(token: string): void {
    this.setToken(token);
    this.loadCurrentUser();
    // Clean up the URL by removing the token
    this.router.navigate([], {
      relativeTo: this.route,
      queryParams: {},
      replaceUrl: true,
    });
  }

  public get currentUserValue(): User | null {
    return this.userSubject.value;
  }

  login(loginRequest: LoginRequest): Observable<AuthResponse> {
    return this.http.post<AuthResponse>(`${this.authUrl}/login`, loginRequest).pipe(
      tap((response) => {
        this.setToken(response.token);
        this.loadCurrentUser();
      })
    );
  }

  register(registerRequest: RegisterRequest): Observable<User> {
    return this.http.post<User>(`${this.authUrl}/register`, registerRequest);
  }

  githubAuth(code: string): Observable<AuthResponse> {
    return this.http.post<AuthResponse>(`${this.oauthUrl}/github/callback`, { code }).pipe(
      tap((response) => {
        console.log(response.token);
        this.setToken(response.token);
        this.loadCurrentUser();
      })
    );
  }

  googleAuth(code: string): Observable<AuthResponse> {
    return this.http.post<AuthResponse>(`${this.oauthUrl}/google/callback`, { code }).pipe(
      tap((response) => {
        this.setToken(response.token);
        this.loadCurrentUser();
      })
    );
  }

  logout(): void {
    localStorage.removeItem(TOKEN_KEY);
    localStorage.removeItem(USER_KEY);
    this.userSubject.next(null);
    this.router.navigate(['/']);
  }

  isLoggedIn(): boolean {
    return !!this.getToken();
  }

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

  public loadCurrentUser(): void {
    this.http.get<User>(`${this.userUrl}/me`).subscribe({
      next: (user) => {
        this.setUser(user);
      },
      error: () => {
        this.logout();
      },
    });
  }

  public verifyEmail(token: string): Observable<unknown> {
    return this.http.post<unknown>(`${this.authUrl}/verify?token=${token}`, null);
  }

  public recoverPassword(email: string): Observable<unknown> {
    return this.http.post<unknown>(`${this.authUrl}/recover-password?email=${email}`, null);
  }

  public resetPassword(token: string, password: string): Observable<unknown> {
    return this.http.post<unknown>(`${this.authUrl}/reset-password`, { token, password });
  }

  private setToken(token: string): void {
    localStorage.setItem(TOKEN_KEY, token);
  }

  private setUser(user: User): void {
    localStorage.setItem(USER_KEY, JSON.stringify(user));
    this.userSubject.next(user);
  }

  private getUserFromStorage(): User | null {
    const user = localStorage.getItem(USER_KEY);
    return user ? JSON.parse(user) : null;
  }

  getUser(): User | null {
    return this.currentUserValue;
  }
}
