import { useEffect, useMemo } from 'react';
import { useDispatch } from 'react-redux';
import { baseApi } from '@ema-platform/api';
import { authStorage } from '../utils/auth-storage';
import { setCurrentProfile } from '../store/auth.slice';
import type { CurrentProfile } from '../types/auth.types';

/**
 * Profile fields the portal knows how to collect.
 *
 * Mirrors `PROFILE_FIELDS` in the API (`module/profile/profile-completeness.ts`).
 * Screens name the fields they need from this list rather than checking
 * properties ad hoc, so "what is missing" has one definition.
 */
export const PROFILE_FIELDS = [
  'firstName',
  'middleName',
  'lastName',
  'gender',
  'dob',
  'pob',
  'maritalStatus',
  'professionId',
  'idType',
  'idNumber',
  'nationality',
  'primaryPhoneNumber',
  'email',
  'regionId',
  'cityId',
  'subCityId',
  'woredaId',
  'streetAddress',
  'emergencyContactName',
  'emergencyContactPhone',
  'emergencyContactRelation',
] as const;

export type ProfileField = (typeof PROFILE_FIELDS)[number];

/**
 * Which `/profile` tab collects a field — used to build deep links.
 *
 * `personal` here means the Personal tab (account details: name, username,
 * email, phone), which is `/profile`'s `'personal'` panel. Name/DOB/gender/
 * profession live on the *Maritime Profile* panel instead, whose tab key is
 * `'profile'` — easy to conflate with the field-section name of the same
 * word, so it's called out here rather than left implicit.
 */
export const PROFILE_FIELD_SECTION: Record<
  ProfileField,
  'personal' | 'profile' | 'address' | 'emergency'
> = {
  firstName: 'profile',
  middleName: 'profile',
  lastName: 'profile',
  gender: 'profile',
  dob: 'profile',
  pob: 'profile',
  maritalStatus: 'profile',
  professionId: 'profile',
  idType: 'address',
  idNumber: 'address',
  nationality: 'address',
  primaryPhoneNumber: 'personal',
  email: 'personal',
  regionId: 'address',
  cityId: 'address',
  subCityId: 'address',
  woredaId: 'address',
  streetAddress: 'address',
  emergencyContactName: 'emergency',
  emergencyContactPhone: 'emergency',
  emergencyContactRelation: 'emergency',
};

/** What a screen needs before it can do its job. */
export interface ProfileRequirement {
  fields: ProfileField[];
  /** Shown to the applicant — why this is being asked for, in plain language. */
  reason: string;
}

export interface ProfileMeResponse {
  profile: CurrentProfile;
  completeness: number;
  missing: ProfileField[];
  /**
   * Every permission key the caller effectively holds (role + portal
   * account-type + position grants), computed server-side. Feeds
   * `usePermissions()`.
   */
  permissions?: string[];
  /**
   * Whether the profile can apply for a CoC/CoP right now: seafarer
   * registration approved, plus a verified sea service record and a
   * verified medical certificate. Computed server-side so the "Apply"
   * button and the API's own eligibility check can never disagree.
   */
  eligibleForCoc: boolean;
}

const profileApi = baseApi
  .enhanceEndpoints({ addTagTypes: ['CurrentProfile'] as const })
  .injectEndpoints({
    endpoints: (builder) => ({
      getMyProfile: builder.query<ProfileMeResponse, void>({
        query: () => ({ url: '/profiles/me' }),
        providesTags: ['CurrentProfile'],
      }),
      /**
       * Saves one tab of `/profile`. Invalidates the profile so the
       * completeness meter and every requirement gate recompute at once.
       */
      updateMyProfile: builder.mutation<
        unknown,
        { id: string; body: Record<string, unknown> }
      >({
        query: ({ id, body }) => ({ url: `/profiles/${id}`, method: 'PUT', body }),
        invalidatesTags: ['CurrentProfile'],
      }),
      /**
       * The portal role — seafarer, vessel owner or logistics operator.
       *
       * Sidebar and route permissions are computed from this server-side
       * (`profiles.type`), not from the declared modes of operation, so the
       * Operations tab writes it here too. Invalidates the profile so the
       * shell repermissions itself without a reload.
       */
      updateMyAccountType: builder.mutation<unknown, { type: string }>({
        query: (body) => ({
          url: '/profiles/me/account-type',
          method: 'PUT',
          body,
        }),
        invalidatesTags: (_result, error) => (error ? [] : ['CurrentProfile']),
      }),
      updateMyAddress: builder.mutation<
        unknown,
        { id: string; body: Record<string, unknown> }
      >({
        query: ({ id, body }) => ({ url: `/addresses/${id}`, method: 'PATCH', body }),
        invalidatesTags: ['CurrentProfile'],
      }),
    }),
    overrideExisting: false,
  });

export const {
  useGetMyProfileQuery,
  useUpdateMyProfileMutation,
  useUpdateMyAddressMutation,
  useUpdateMyAccountTypeMutation,
} = profileApi;
export const currentProfileApi = profileApi;

/**
 * The one way to get at the signed-in user's profile.
 *
 * Replaces `authStorage.getProfileId()`, which only ever held a value if the
 * user had been through the (now deleted) setup wizard. Pages that read it
 * directly did `if (!profileId) return;` and rendered blank forever for anyone
 * who signed up afterwards.
 *
 * Resolution order: RTK Query cache → `authStorage` (so the id is available
 * synchronously on the very first render) → `GET /profiles/me`, which
 * provisions a profile if the user has none. The resolved id is written back
 * to storage. Never blocks render: `profileId` may be undefined for a tick,
 * and callers should show a loading or empty state rather than bail out.
 */
export function useCurrentProfile() {
  const dispatch = useDispatch();
  const { data, isLoading, isFetching, error, refetch } = useGetMyProfileQuery();

  const profileId = data?.profile?.id ?? authStorage.getProfileId();

  useEffect(() => {
    if (!data?.profile) return;
    authStorage.setProfileId(data.profile.id);
    dispatch(setCurrentProfile(data.profile));
  }, [data?.profile, dispatch]);

  const missing = useMemo(() => data?.missing ?? [], [data?.missing]);

  return {
    profileId,
    profile: data?.profile,
    isLoading,
    isFetching,
    error,
    refetch,
    completeness: data?.completeness ?? 0,
    missing,
    eligibleForCoc: data?.eligibleForCoc ?? false,
    /**
     * True when nothing the requirement asks for is still blank. Unknown
     * profile (still loading) reads as not-ready, so a caller never submits
     * against data it has not seen.
     */
    isReadyFor: (requirement: ProfileRequirement) =>
      Boolean(data) && requirement.fields.every((field) => !missing.includes(field)),
    /** The subset of a requirement that is still outstanding. */
    gapsFor: (requirement: ProfileRequirement) =>
      requirement.fields.filter((field) => missing.includes(field)),
  };
}
