'use server';

import { revalidatePath } from 'next/cache';

import { listRegions } from '@lib/data/regions';
import { StoreCart, StoreOrder } from '@medusajs/types';

import { getLogger } from '@kit/shared/logger';
import { Tables } from '@kit/supabase/database';
import { getSupabaseServerClient } from '@kit/supabase/server-client';

import { EnrichedCartItem } from '../../app/home/(user)/_components/cart/types';
import { loadCurrentUserAccount } from '../../app/home/(user)/_lib/server/load-user-account';
import { createCartEntriesLog } from './audit/cartEntries';
import { handleDeleteCartItem } from './medusaCart.service';

type Locations = Tables<{ schema: 'medreport' }, 'connected_online_locations'>;
type Services = Tables<{ schema: 'medreport' }, 'connected_online_services'>;
type ServiceProviders = Tables<
  { schema: 'medreport' },
  'connected_online_service_providers'
>;

export async function getCartReservations(
  medusaCart: StoreCart,
): Promise<EnrichedCartItem[]> {
  const supabase = getSupabaseServerClient();

  const cartLineItemIds = medusaCart.items?.map(({ id }) => id);

  if (!cartLineItemIds?.length) {
    return [];
  }

  const { data: reservations } = await supabase
    .schema('medreport')
    .from('connected_online_reservation')
    .select(
      'id, startTime:start_time, service:service_id, location:location_sync_id, serviceProvider:service_user_id, medusaCartLineItemId:medusa_cart_line_item_id',
    )
    .in('medusa_cart_line_item_id', cartLineItemIds)
    .throwOnError();

  const locationSyncIds: number[] =
    reservations
      ?.filter((reservation) => !!reservation.location)
      .map((reservation) => reservation.location!) ?? [];
  const serviceIds =
    reservations?.map((reservation) => reservation.service) ?? [];
  const serviceProviderIds =
    reservations.map((reservation) => reservation.serviceProvider) ?? [];

  let locations:
    | {
        syncId: Locations['sync_id'];
        name: Locations['name'];
        address: Locations['address'];
      }[]
    | null = null;
  if (locationSyncIds.length) {
    ({ data: locations } = await supabase
      .schema('medreport')
      .from('connected_online_locations')
      .select('syncId:sync_id, name, address')
      .in('sync_id', locationSyncIds)
      .throwOnError());
  }

  let services:
    | {
        id: Services['id'];
        name: Services['name'];
      }[]
    | null = null;
  if (serviceIds.length) {
    ({ data: services } = await supabase
      .schema('medreport')
      .from('connected_online_services')
      .select('name, id')
      .in('id', serviceIds)
      .throwOnError());
  }

  let serviceProviders:
    | {
        id: ServiceProviders['id'];
        name: ServiceProviders['name'];
        jobTitleEt: ServiceProviders['job_title_et'];
        jobTitleEn: ServiceProviders['job_title_en'];
        jobTitleRu: ServiceProviders['job_title_ru'];
        spokenLanguages: ServiceProviders['spoken_languages'];
      }[]
    | null = null;
  if (serviceProviderIds.length) {
    ({ data: serviceProviders } = await supabase
      .schema('medreport')
      .from('connected_online_service_providers')
      .select(
        'id, name, jobTitleEt:job_title_et, jobTitleEn:job_title_en, jobTitleRu:job_title_ru, spokenLanguages:spoken_languages',
      )
      .in('id', serviceProviderIds)
      .throwOnError());
  }

  const results = [];
  for (const reservation of reservations) {
    if (reservation.medusaCartLineItemId === null) {
      continue;
    }

    const cartLineItem = medusaCart.items?.find(
      (item) => item.id === reservation.medusaCartLineItemId,
    );

    if (!cartLineItem) {
      continue;
    }

    const location = locations?.find(
      (location) => location.syncId === reservation.location,
    );
    const service = services?.find(
      (service) => service.id === reservation.service,
    );
    const serviceProvider = serviceProviders?.find(
      (serviceProvider) => serviceProvider.id === reservation.serviceProvider,
    );

    const countryCodes = await listRegions().then((regions) =>
      regions?.map((r) => r.countries?.map((c) => c.iso_2)).flat(),
    );

    const enrichedReservation = {
      ...reservation,
      location,
      service,
      serviceProvider,
    };

    results.push({
      ...cartLineItem,
      reservation: {
        ...enrichedReservation,
        medusaCartLineItemId: reservation.medusaCartLineItemId!,
        countryCode: countryCodes[0],
      },
    });
  }

  return results;
}

export async function createInitialReservation({
  serviceId,
  clinicId,
  appointmentUserId,
  syncUserID,
  startTime,
  medusaLineItemId,
  locationId,
  comments = '',
}: {
  serviceId: number;
  clinicId: number;
  appointmentUserId: number;
  syncUserID: number;
  startTime: Date;
  medusaLineItemId: string;
  locationId?: number | null;
  comments?: string;
}) {
  const logger = await getLogger();
  const supabase = getSupabaseServerClient();

  const {
    data: { user },
  } = await supabase.auth.getUser();

  const userId = user?.id;

  if (!userId) {
    throw new Error('User not authenticated');
  }

  logger.info(
    'Creating reservation' +
      JSON.stringify({ serviceId, clinicId, startTime, userId }),
  );

  try {
    const { data: createdReservation } = await supabase
      .schema('medreport')
      .from('connected_online_reservation')
      .insert({
        clinic_id: clinicId,
        comments,
        lang: 'et',
        service_id: serviceId,
        service_user_id: appointmentUserId,
        start_time: startTime.toString(),
        sync_user_id: syncUserID,
        user_id: userId,
        status: 'PENDING',
        medusa_cart_line_item_id: medusaLineItemId,
        location_sync_id: locationId,
      })
      .select('id')
      .single()
      .throwOnError();

    logger.info(
      `Created reservation ${JSON.stringify({ createdReservation, userId })}`,
    );

    return createdReservation;
  } catch (e) {
    logger.error(
      `Failed to create initial reservation ${JSON.stringify({ serviceId, clinicId, startTime })} ${e}`,
    );
    await handleDeleteCartItem({ lineId: medusaLineItemId });
    throw e;
  }
}

export async function cancelReservation(medusaLineItemId: string) {
  const supabase = getSupabaseServerClient();

  return supabase
    .schema('medreport')
    .from('connected_online_reservation')
    .update({
      status: 'CANCELLED',
    })
    .eq('medusa_cart_line_item_id', medusaLineItemId)
    .throwOnError();
}

export async function getOrderedTtoServices({
  cart,
  medusaOrder,
}: {
  cart?: StoreCart;
  medusaOrder?: StoreOrder;
}) {
  const supabase = getSupabaseServerClient();

  if (!medusaOrder && !cart) {
    throw new Error('No cart or medusa order provided');
  }

  const ttoReservationIds: number[] =
    (medusaOrder?.items ?? cart?.items)
      ?.filter(({ metadata }) => !!metadata?.connectedOnlineReservationId)
      .map(({ metadata }) => Number(metadata!.connectedOnlineReservationId)) ??
    [];

  const { data: orderedTtoServices } = await supabase
    .schema('medreport')
    .from('connected_online_reservation')
    .select('*, provider:connected_online_providers(key)')
    .in('id', ttoReservationIds)
    .throwOnError();

  return orderedTtoServices;
}

export async function updateReservationTime({
  reservationId,
  newStartTime,
  newServiceId,
  newAppointmentUserId,
  newSyncUserId,
  newLocationId, // TODO stop allowing null when Connected starts returning the correct ids instead of -1
  cartId,
}: {
  reservationId: number;
  newStartTime: Date;
  newServiceId: number;
  newAppointmentUserId: number;
  newSyncUserId: number;
  newLocationId: number | null;
  cartId: string;
}) {
  const logger = await getLogger();
  const supabase = getSupabaseServerClient();

  const {
    data: { user },
  } = await supabase.auth.getUser();

  const userId = user?.id;
  const { account } = await loadCurrentUserAccount();

  if (!userId || !account) {
    throw new Error('User not authenticated');
  }

  const reservationData = JSON.stringify({
    reservationId,
    newStartTime,
    newServiceId,
    newAppointmentUserId,
    newSyncUserId,
    newLocationId,
    userId,
    cartId,
  });

  logger.info('Updating reservation' + reservationData);
  try {
    await supabase
      .schema('medreport')
      .from('connected_online_reservation')
      .update({
        service_id: newServiceId,
        service_user_id: newAppointmentUserId,
        sync_user_id: newSyncUserId,
        start_time: newStartTime.toString(),
        location_sync_id: newLocationId,
      })
      .eq('id', reservationId)
      .eq('user_id', user.id)
      .throwOnError();

    logger.info(`Successfully updated reservation ${reservationData}`);
    await createCartEntriesLog({
      operation: 'CHANGE_RESERVATION',
      accountId: account.id,
      cartId: cartId,
      comment: `${reservationData}`,
    });
    revalidatePath('/home/cart', 'layout');
  } catch (e) {
    logger.error(`Failed to update reservation ${reservationData}`);
    await createCartEntriesLog({
      operation: 'CHANGE_RESERVATION',
      accountId: account.id,
      cartId: cartId,
      comment: `${e}`,
    });
    throw e;
  }
}
