import { Injectable, signal } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { firstValueFrom } from 'rxjs';
import {
  LoginRequest,
  RegisterRequest,
  TokenResponse,
  UserInfo,
} from '../models/auth.models';

const REFRESH_TOKEN_KEY = 'dm_refresh_token';

@Injectable({ providedIn: 'root' })
export class AuthService {
  private readonly accessToken = signal<string | null>(null);
  private readonly refreshToken = signal<string | null>(null);
  readonly user = signal<UserInfo | null>(null);

  constructor(private readonly http: HttpClient) {
    const stored = localStorage.getItem(REFRESH_TOKEN_KEY);
    if (stored) {
      this.refreshToken.set(stored);
    }
  }

  isAuthenticated(): boolean {
    return Boolean(this.accessToken());
  }

  getAccessToken(): string | null {
    return this.accessToken();
  }

  getRefreshToken(): string | null {
    return this.refreshToken();
  }

  setTokens(tokens: TokenResponse): void {
    this.accessToken.set(tokens.accessToken);
    this.refreshToken.set(tokens.refreshToken);
    localStorage.setItem(REFRESH_TOKEN_KEY, tokens.refreshToken);
  }

  clearAuth(): void {
    this.accessToken.set(null);
    this.refreshToken.set(null);
    this.user.set(null);
    localStorage.removeItem(REFRESH_TOKEN_KEY);
  }

  async login(payload: LoginRequest): Promise<UserInfo> {
    const tokens = await firstValueFrom(this.http.post<TokenResponse>('/api/auth/login', payload));
    this.setTokens(tokens);
    return this.loadCurrentUser();
  }

  async register(payload: RegisterRequest): Promise<UserInfo> {
    const tokens = await firstValueFrom(this.http.post<TokenResponse>('/api/auth/register', payload));
    this.setTokens(tokens);
    return this.loadCurrentUser();
  }

  async loadCurrentUser(): Promise<UserInfo> {
    const me = await firstValueFrom(this.http.get<UserInfo>('/api/auth/me'));
    this.user.set(me);
    return me;
  }

  async restoreSession(): Promise<boolean> {
    const refresh = this.refreshToken();
    if (!refresh) return false;
    const token = await this.refreshAccessToken();
    if (!token) {
      this.clearAuth();
      return false;
    }
    try {
      await this.loadCurrentUser();
      return true;
    } catch {
      this.clearAuth();
      return false;
    }
  }

  async logout(): Promise<void> {
    const refresh = this.refreshToken();
    try {
      if (refresh) {
        await firstValueFrom(this.http.post('/api/auth/logout', { refreshToken: refresh }));
      }
    } catch {
      // best effort
    }
    this.clearAuth();
  }

  async refreshAccessToken(): Promise<string | null> {
    const refresh = this.refreshToken();
    if (!refresh) return null;
    try {
      const tokens = await firstValueFrom(
        this.http.post<TokenResponse>('/api/auth/refresh', { refreshToken: refresh }),
      );
      this.setTokens(tokens);
      return tokens.accessToken;
    } catch {
      return null;
    }
  }
}
