import { HttpErrorResponse, HttpInterceptorFn } from '@angular/common/http';
import { inject } from '@angular/core';
import { catchError, from, map, Observable, of, switchMap, tap, throwError } from 'rxjs';
import { ToastService } from '../services/toast.service';

/**
 * Surfaces every failed HTTP response as a toast so errors are never silent.
 * 401s are left to the auth interceptor (token refresh / redirect to login).
 *
 * Must be registered BEFORE the auth interceptor so its catchError runs LAST
 * (outermost) — i.e. after auth has had a chance to refresh-and-retry a 401.
 */
export const httpErrorInterceptor: HttpInterceptorFn = (req, next) => {
  const toastService = inject(ToastService);

  return next(req).pipe(
    catchError((error: HttpErrorResponse) => {
      if (error.status === 401) {
        // Handled by the auth interceptor (silent refresh or redirect to login).
        return throwError(() => error);
      }

      return resolveMessage(error).pipe(
        tap(message => toastService.error(message)),
        switchMap(() => throwError(() => error)),
      );
    }),
  );
};

/**
 * Builds a human-readable message from the error response. The body can be a
 * parsed object, a string, or a Blob (when the request used responseType:'blob',
 * e.g. PDF / signature downloads) — Blobs must be read asynchronously.
 */
function resolveMessage(error: HttpErrorResponse): Observable<string> {
  const body = error.error;

  if (body instanceof Blob) {
    return from(body.text()).pipe(map(text => parseBodyText(text, error.status)));
  }
  if (typeof body === 'string') {
    return of(parseBodyText(body, error.status));
  }
  if (body && typeof body === 'object') {
    const message = (body as { message?: unknown }).message;
    const title = (body as { title?: unknown }).title;
    if (typeof message === 'string' && message.length > 0) {
      return of(message);
    }
    if (typeof title === 'string' && title.length > 0) {
      return of(title);
    }
  }
  return of(defaultMessage(error.status));
}

function parseBodyText(text: string, status: number): string {
  if (text) {
    try {
      const parsed = JSON.parse(text) as { message?: unknown; title?: unknown };
      if (typeof parsed.message === 'string' && parsed.message.length > 0) {
        return parsed.message;
      }
      if (typeof parsed.title === 'string' && parsed.title.length > 0) {
        return parsed.title;
      }
    } catch {
      // Not JSON — fall through to the status-based default.
    }
  }
  return defaultMessage(status);
}

function defaultMessage(status: number): string {
  switch (status) {
    case 0:
      return 'Cannot reach the server / 無法連線到伺服器';
    case 400:
      return 'Invalid request / 請求格式錯誤';
    case 403:
      return 'You do not have permission for this action / 沒有權限執行此操作';
    case 404:
      return 'The requested item was not found / 找不到要求的資料';
    case 409:
      return 'This action conflicts with the current state / 操作與目前狀態衝突';
    case 422:
      return 'The submitted data is invalid / 提交的資料無效';
    case 500:
      return 'A server error occurred / 伺服器發生錯誤';
    default:
      return `Request failed (HTTP ${status}) / 請求失敗 (HTTP ${status})`;
  }
}
