'use server';

import logRequestResult from '@/lib/services/audit.service';
import { RequestStatus } from '@/lib/types/audit';
import {
  AvailableAppointmentsResponse,
  BookTimeResponse,
  ConfirmedLoadResponse,
  ConnectedOnlineMethodName,
} from '@/lib/types/connected-online';
import { ExternalApi } from '@/lib/types/external';
import { Tables } from '@/packages/supabase/src/database.types';
import { createClient } from '@/utils/supabase/server';
import axios from 'axios';

export async function getAvailableAppointmentsForService(
  serviceId: number,
  startTime?: Date,
) {
  try {
    const showTimesFrom = startTime ? { StartTime: startTime } : {};

    const response = await axios.post(
      `${process.env.CONNECTED_ONLINE_URL!}/${ConnectedOnlineMethodName.GetAvailabilities}`,
      {
        headers: {
          'Content-Type': 'application/json; charset=utf-8',
        },
        param: JSON.stringify({
          ServiceID: serviceId,
          Key: '7T624nlu',
          Lang: 'et',
          ...showTimesFrom,
        }),
      },
    );

    const responseData: AvailableAppointmentsResponse = JSON.parse(
      response.data.d,
    );

    if (
      responseData?.ErrorCode !== 0 ||
      !responseData.Data.T_Service?.length ||
      !responseData.Data.T_Booking?.length
    ) {
      let comment = `Response returned error code ${responseData.ErrorCode}, message: ${responseData.ErrorMessage}`;
      if (responseData?.ErrorCode === 0) {
        comment = responseData.Data.T_Service?.length
          ? `No service present in appointment availability response, service id: ${serviceId}, start time: ${startTime}`
          : `No booking times present in appointment availability response, service id: ${serviceId}, start time: ${startTime}`;
      }

      await logRequestResult(
        ExternalApi.ConnectedOnline,
        ConnectedOnlineMethodName.GetAvailabilities,
        RequestStatus.Fail,
        comment,
      );

      return null;
    }

    await logRequestResult(
      ExternalApi.ConnectedOnline,
      ConnectedOnlineMethodName.GetAvailabilities,
      RequestStatus.Success,
      JSON.stringify(responseData),
    );
    return responseData.Data;
  } catch (error) {
    await logRequestResult(
      ExternalApi.ConnectedOnline,
      ConnectedOnlineMethodName.GetAvailabilities,
      RequestStatus.Fail,
      JSON.stringify(error),
    );
    return null;
  }
}

export async function bookAppointment(
  serviceSyncId: number,
  clinicId: number,
  appointmentUserId: number,
  syncUserID: number,
  startTime: string,
  locationId = 0,
  comments = '',
  isEarlierTimeRequested = false,
  earlierTimeRequestComment = '',
) {
  const supabase = await createClient();

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

    if (!user?.id) {
      throw new Error('User not authenticated');
    }

    const [
      { data: dbClinic, error: clinicError },
      { data: dbService, error: serviceError },
    ] = await Promise.all([
      supabase
        .schema('medreport')
        .from('connected_online_providers')
        .select('*')
        .eq('id', clinicId)
        .limit(1),
      supabase
        .schema('medreport')
        .from('connected_online_services')
        .select('*')
        .eq('sync_id', serviceSyncId)
        .eq('clinic_id', clinicId)
        .limit(1),
    ]);

    if (!dbClinic?.length || !dbService?.length) {
      return logRequestResult(
        ExternalApi.ConnectedOnline,
        ConnectedOnlineMethodName.BookTime,
        RequestStatus.Fail,
        dbClinic?.length
          ? `Could not find clinic with id ${clinicId}, error: ${JSON.stringify(clinicError)}`
          : `Could not find service with sync id ${serviceSyncId} and clinic id ${clinicId}, error: ${JSON.stringify(serviceError)}`,
        startTime,
        serviceSyncId,
        clinicId,
      );
    }

    const clinic: Tables<
      { schema: 'medreport' },
      'connected_online_providers'
    > = dbClinic![0];
    const service: Tables<
      { schema: 'medreport' },
      'connected_online_services'
    > = dbService![0];

    // TODO the dummy data needs to be replaced with real values once they're present on the user/account
    const response = await axios.post(
      `${process.env.CONNECTED_ONLINE_URL!}/${ConnectedOnlineMethodName.BookTime}`,
      {
        headers: {
          'Content-Type': 'application/json; charset=utf-8',
        },
        param: JSON.stringify({
          EarlierTime: isEarlierTimeRequested, // once we have the e-shop functionality we can let the user select if she would like to be offered earlier time slots if they become available
          EarlierTimeComment: earlierTimeRequestComment,
          ClinicID: clinic.id,
          ServiceID: service.id,
          ClinicServiceID: service.sync_id,
          UserID: appointmentUserId,
          SyncUserID: syncUserID,
          StartTime: startTime,
          FirstName: 'Test',
          LastName: 'User',
          PersonalCode: '4',
          Email: user.email,
          Phone: 'phone',
          Comments: comments,
          Location: locationId,
          FreeCode: '',
          AddToBasket: false,
          Key: '7T624nlu',
          Lang: 'et', // update when integrated into app, if needed
        }),
      },
    );

    const responseData: BookTimeResponse = JSON.parse(response.data.d);

    if (responseData?.ErrorCode !== 0 || !responseData.Value) {
      return logRequestResult(
        ExternalApi.ConnectedOnline,
        ConnectedOnlineMethodName.BookTime,
        RequestStatus.Fail,
        JSON.stringify(responseData),
        startTime,
        service.id,
        clinicId,
      );
    }

    const responseParts = responseData.Value.split(',');

    const { error } = await supabase
      .schema('medreport')
      .from('connected_online_reservation')
      .insert({
        booking_code: responseParts[1],
        clinic_id: clinic.id,
        comments,
        lang: 'et', // change later, if needed
        service_id: service.id,
        service_user_id: appointmentUserId,
        start_time: startTime,
        sync_user_id: syncUserID,
        requires_payment: !!responseParts[0],
        user_id: user.id,
      });

    await logRequestResult(
      ExternalApi.ConnectedOnline,
      ConnectedOnlineMethodName.BookTime,
      RequestStatus.Success,
      JSON.stringify(responseData),
      startTime,
      service.id,
      clinicId,
    );

    if (error) {
      throw new Error(error.message);
    }

    return responseData.Value;
  } catch (error) {
    return logRequestResult(
      ExternalApi.ConnectedOnline,
      ConnectedOnlineMethodName.BookTime,
      RequestStatus.Fail,
      JSON.stringify(error),
      startTime,
      serviceSyncId,
      clinicId,
    );
  }
}

export async function getConfirmedService(reservationCode: string) {
  try {
    const response = await axios.post(
      `${process.env.CONNECTED_ONLINE_URL!}/${ConnectedOnlineMethodName.ConfirmedLoad}`,
      {
        headers: {
          'Content-Type': 'application/json; charset=utf-8',
        },
        param: JSON.stringify({ Value: `${reservationCode}|7T624nlu|et` }),
      },
    );

    const responseData: ConfirmedLoadResponse = JSON.parse(response.data.d);

    if (responseData?.ErrorCode !== 0) {
      await logRequestResult(
        ExternalApi.ConnectedOnline,
        ConnectedOnlineMethodName.ConfirmedLoad,
        RequestStatus.Fail,
        JSON.stringify(responseData),
      );
      return null;
    }

    await logRequestResult(
      ExternalApi.ConnectedOnline,
      ConnectedOnlineMethodName.ConfirmedLoad,
      RequestStatus.Success,
      JSON.stringify(responseData),
    );
    return responseData.Data;
  } catch (error) {
    await logRequestResult(
      ExternalApi.ConnectedOnline,
      ConnectedOnlineMethodName.ConfirmedLoad,
      RequestStatus.Fail,
      JSON.stringify(error),
    );
    return null;
  }
}
