import { inject, Injectable } from '@angular/core';
import { StorageProvider } from './storage-provider';
import {
  ResponseArgsOfUserState,
  UserStateService,
} from '@generated/swagger/isa-api';
import {
  catchError,
  filter,
  firstValueFrom,
  retry,
  switchMap,
  tap,
  throwError,
  timer,
} from 'rxjs';
import { Debounce, ValidateParam } from '@isa/common/decorators';
import { AuthService } from '@core/auth';
import z from 'zod';
import { logger } from '@isa/core/logging';
import { HttpErrorResponse } from '@angular/common/http';

type UserState = Record<string, unknown>;

const DEFAULT_USER_STATE_RESPONSE: ResponseArgsOfUserState = {
  result: { content: '{}' },
  error: false,
  message: 'User state not found',
};

@Injectable({ providedIn: 'root' })
export class UserStorageProvider implements StorageProvider {
  #logger = logger(() => ({ service: 'UserStorageProvider' }));
  #userStateService = inject(UserStateService);
  #authService = inject(AuthService);

  #loadUserState = this.#authService.authenticated$.pipe(
    filter((authenticated) => authenticated),
    switchMap(() =>
      this.#userStateService.UserStateGetUserState().pipe(
        catchError((error) => {
          if (error instanceof HttpErrorResponse && error.status === 404) {
            return [DEFAULT_USER_STATE_RESPONSE];
          }
          return throwError(() => error);
        }),
        retry({
          count: 3,
          delay: (error, retryCount) => {
            this.#logger.warn('Retrying user state load', () => ({
              attempt: retryCount,
              error: error.message,
            }));
            return timer(1000 * retryCount); // Exponential backoff with timer
          },
        }),
      ),
    ),
    tap((res) => {
      if (res?.result?.content) {
        const parsed = JSON.parse(res.result.content);
        if (parsed && typeof parsed === 'object') {
          this.#state = parsed;
        } else {
          this.#state = {};
        }
      }
    }),
  );

  #state: UserState = {};

  async init() {
    this.#logger.info('Initializing UserStorageProvider');
    await firstValueFrom(this.#loadUserState);
    this.#logger.info('UserStorageProvider initialized');
  }

  async reload(): Promise<void> {
    this.#logger.info('Reloading user state');
    await firstValueFrom(this.#loadUserState);
    this.#logger.info('User state reloaded');
  }

  #setCurrentState(state: UserState) {
    const newState = structuredClone(state);
    Object.freeze(newState);
    this.#state = newState;
  }

  #setState(state: UserState) {
    this.#setCurrentState(state);
    this.postNewState();
  }

  @Debounce({ wait: 1000 })
  private postNewState(): void {
    this.#logger.debug('Saving user state to server');
    const state = JSON.stringify(this.#state);
    firstValueFrom(
      this.#userStateService.UserStateSetUserState({
        content: state,
      }),
    )
      .then(() => {
        this.#logger.debug('User state saved successfully');
      })
      .catch((error) => {
        this.#logger.error('Failed to save user state', error);
      });
  }

  @ValidateParam(0, z.string().min(1))
  set(key: string, value: Record<string, unknown>): void {
    this.#logger.debug('Setting user state key', () => ({ key }));
    const current = this.#state;
    const content = structuredClone(current);
    content[key] = value;

    this.#setState(content);
  }

  @ValidateParam(0, z.string().min(1))
  get(key: string): unknown {
    this.#logger.trace('Getting user state key', () => ({ key }));
    return structuredClone(this.#state[key]);
  }

  @ValidateParam(0, z.string().min(1))
  clear(key: string): void {
    this.#logger.debug('Clearing user state key', () => ({ key }));
    const current = this.#state;
    if (key in current) {
      const content = structuredClone(current);
      delete content[key];
      this.#setState(content);
    }
  }
}
