/**
 * Shared applicant/admin auth operations — registration, OTP, password reset, and the
 * reference catalogs — implemented against the same-origin backend via the shared
 * {@link apiClient}.
 *
 * Ported (and slimmed) from fe0/src/lib/auth-service.ts. Differences from fe0:
 *   - Uses the shared `apiClient` (Bearer + auto-refresh interceptors) instead of bespoke
 *     `fetch` calls, so these compose with the rest of the kernel.
 *   - Throw-on-error rather than fe0's `{ success, error }` discriminated unions. Callers
 *     catch and render the message via `detailFromApiError`. `register` throws a richer
 *     {@link AuthRegisterError} so the form can show the HTTP-status heading + reason list.
 */
import { apiClient, type ApiError } from '../api/client';
import type { AcademicTitleOption, UnitOption } from '../profile/types';

export type OtpDeliveryChannel = 'smtp' | 'log_only' | 'none' | 'smtp_failed';

export interface RegisterPayload {
  fullName: string;
  email: string;
  password: string;
  passwordConfirm: string;
  employeeId: string;
  academicTitleCode: string;
  academicTitleOther?: string | null;
  unitId?: string | null;
  unitNameFreetext?: string | null;
  jobTitle: string;
}

/** Successful `register` response when the backend requires OTP email verification. */
export interface RegisterOtpResult {
  message: string;
  email: string;
  emailVerificationRequired: true;
  otpTtlSeconds?: number;
  otpDeliveryChannel?: OtpDeliveryChannel;
}

/**
 * Thrown by {@link register} on an HTTP error. Carries the status (for the UI heading)
 * and the parsed reason list from FastAPI's `{ detail }` (string or validation array).
 */
export class AuthRegisterError extends Error {
  readonly httpStatus: number;
  readonly reasons: string[];

  constructor(message: string, httpStatus: number, reasons: string[]) {
    super(message);
    this.name = 'AuthRegisterError';
    this.httpStatus = httpStatus;
    this.reasons = reasons.length > 0 ? reasons : [message];
  }
}

function isApiError(err: unknown): err is ApiError {
  return Boolean(err) && typeof err === 'object' && typeof (err as ApiError).status === 'number';
}

/** FastAPI detail → deduped reason list (handles both the string and `[{ msg }]` shapes). */
function reasonsFromDetail(detail: unknown, fallback: string): string[] {
  if (typeof detail === 'string') {
    const s = detail.trim();
    return s ? [s] : [fallback];
  }
  if (Array.isArray(detail)) {
    const reasons = detail
      .map((e) => {
        if (typeof e === 'string') return e.trim();
        if (e && typeof e === 'object' && 'msg' in e) {
          const m = (e as { msg?: unknown }).msg;
          return typeof m === 'string' ? m.trim() : String(m);
        }
        return '';
      })
      .filter((s) => s.length > 0);
    const dedup = [...new Set(reasons)];
    return dedup.length ? dedup : [fallback];
  }
  return [fallback];
}

const AUTH = '/api/v1/auth';

/**
 * POST /api/v1/auth/register. Resolves with the OTP-verification result on success;
 * throws {@link AuthRegisterError} on an HTTP error. The server derives the role from the
 * email policy and ignores any client-supplied role.
 */
export async function register(payload: RegisterPayload): Promise<RegisterOtpResult> {
  try {
    const raw = await apiClient.post<Record<string, unknown>>(`${AUTH}/register`, {
      fullName: payload.fullName,
      email: payload.email,
      password: payload.password,
      passwordConfirm: payload.passwordConfirm,
      employeeId: payload.employeeId,
      academicTitleCode: payload.academicTitleCode,
      academicTitleOther: payload.academicTitleOther ?? undefined,
      unitId: payload.unitId ?? undefined,
      unitNameFreetext: payload.unitNameFreetext ?? undefined,
      jobTitle: payload.jobTitle,
    });

    const otpTtlRaw = raw.otpTtlSeconds;
    const otpTtlSeconds =
      typeof otpTtlRaw === 'number' && Number.isFinite(otpTtlRaw) && otpTtlRaw > 0
        ? Math.floor(otpTtlRaw)
        : undefined;
    const ch = raw.otpDeliveryChannel;
    const otpDeliveryChannel =
      ch === 'smtp' || ch === 'log_only' || ch === 'none' || ch === 'smtp_failed' ? ch : undefined;

    return {
      message: typeof raw.message === 'string' ? raw.message : '',
      email: typeof raw.email === 'string' ? raw.email : payload.email.trim().toLowerCase(),
      emailVerificationRequired: true,
      otpTtlSeconds,
      otpDeliveryChannel,
    };
  } catch (err) {
    if (isApiError(err)) {
      const detail = (err.data as { detail?: unknown } | undefined)?.detail;
      const fallback = err.message?.trim() || 'Đăng ký thất bại. Vui lòng thử lại.';
      const reasons = reasonsFromDetail(detail, fallback);
      throw new AuthRegisterError(reasons[0], err.status, reasons);
    }
    throw err;
  }
}

/** POST /api/v1/auth/verify-otp → resolves with the server message; throws on error. */
export async function verifyOtp(payload: { email: string; otp: string }): Promise<string> {
  const raw = await apiClient.post<{ message?: string }>(`${AUTH}/verify-otp`, {
    email: payload.email.trim().toLowerCase(),
    otp: payload.otp.trim(),
  });
  return typeof raw.message === 'string' ? raw.message : '';
}

/** POST /api/v1/auth/resend-otp → resolves with the server message; throws on error. */
export async function resendOtp(email: string): Promise<string> {
  const raw = await apiClient.post<{ message?: string }>(`${AUTH}/resend-otp`, {
    email: email.trim().toLowerCase(),
  });
  return typeof raw.message === 'string' ? raw.message : '';
}

/** POST /api/v1/auth/forgot-password → resolves with the server message; throws on error. */
export async function forgotPassword(email: string): Promise<string> {
  const raw = await apiClient.post<{ message?: string }>(`${AUTH}/forgot-password`, {
    email: email.trim().toLowerCase(),
  });
  return typeof raw.message === 'string' ? raw.message : '';
}

/** POST /api/v1/auth/reset-password → resolves with the server message; throws on error. */
export async function resetPassword(payload: {
  token: string;
  newPassword: string;
  newPasswordConfirm: string;
}): Promise<string> {
  const raw = await apiClient.post<{ message?: string }>(`${AUTH}/reset-password`, {
    token: payload.token,
    newPassword: payload.newPassword,
    newPasswordConfirm: payload.newPasswordConfirm,
  });
  return typeof raw.message === 'string' ? raw.message : '';
}

/** GET /api/v1/auth/reference/academic-titles. */
export async function getAcademicTitles(): Promise<AcademicTitleOption[]> {
  const raw = await apiClient.get<AcademicTitleOption[]>(`${AUTH}/reference/academic-titles`);
  return Array.isArray(raw) ? raw : [];
}

/** GET /api/v1/auth/reference/units. */
export async function getUnits(): Promise<UnitOption[]> {
  const raw = await apiClient.get<UnitOption[]>(`${AUTH}/reference/units`);
  return Array.isArray(raw) ? raw : [];
}
