import axios from 'axios';

import { getSupabaseServerAdminClient } from '@kit/supabase/server-admin-client';

import { logSyncResult } from '~/lib/services/audit.service';
import { SyncStatus } from '~/lib/types/audit';
import type {
  ISearchLoadResponse,
  P_JobTitleTranslation,
} from '~/lib/types/connected-online';

function createTranslationMap(translations: P_JobTitleTranslation[]) {
  const result: Map<
    number,
    Map<number, { textEN: string; textRU: string; textET: string }>
  > = new Map();

  for (const translation of translations) {
    const { ClinicID, TextET, TextEN, TextRU, SyncID } = translation;

    if (!result.has(ClinicID)) {
      result.set(ClinicID, new Map());
    }

    result.get(ClinicID)!.set(SyncID, {
      textET: TextET,
      textEN: TextEN,
      textRU: TextRU,
    });
  }

  return result;
}

function getSpokenLanguages(spokenLanguages?: string) {
  if (!spokenLanguages || !spokenLanguages.length) return [];
  return spokenLanguages.split(',');
}

export default async function syncConnectedOnline() {
  const isProd = !['test', 'localhost'].some((pathString) =>
    process.env.NEXT_PUBLIC_SITE_URL?.includes(pathString),
  );

  const baseUrl = process.env.CONNECTED_ONLINE_URL;

  if (!baseUrl) {
    throw new Error('Could not access all necessary environment variables');
  }

  const supabase = getSupabaseServerAdminClient();

  try {
    const searchLoadResponse = await axios.post<{ d: string }>(
      `${baseUrl}/Search_Load`,
      {
        headers: {
          'Content-Type': 'application/json; charset=utf-8',
        },
        param: "{'Value':'|et|-1'}", // get all available services in Estonian
      },
    );

    const responseData: ISearchLoadResponse = JSON.parse(
      searchLoadResponse.data.d,
    );

    if (responseData?.ErrorCode !== 0) {
      throw new Error('Failed to get Connected Online data');
    }

    if (
      !responseData.Data.T_Lic?.length ||
      !responseData.Data.T_Service?.length
    ) {
      return supabase.schema('audit').from('sync_entries').insert({
        operation: 'CONNECTED_ONLINE_SYNC',
        comment: 'No clinic or service data received',
        status: 'FAIL',
        changed_by_role: 'service_role',
      });
    }

    let clinics;
    let services;
    let serviceProviders;
    let jobTitleTranslations;
    // Filter out "Dentas Demo OÜ" in prod or only sync "Dentas Demo OÜ" in any other environment
    const isDemoClinic = (clinicId: number) =>
      isProd ? clinicId !== 2 : clinicId === 2;
    clinics = responseData.Data.T_Lic.filter(({ ID }) => isDemoClinic(ID));
    services = responseData.Data.T_Service.filter(({ ClinicID }) =>
      isDemoClinic(ClinicID),
    );
    serviceProviders = responseData.Data.T_Doctor.filter(({ ClinicID }) =>
      isDemoClinic(ClinicID),
    );
    jobTitleTranslations = createTranslationMap(
      responseData.Data.P_JobTitleTranslations.filter(({ ClinicID }) =>
        isDemoClinic(ClinicID),
      ),
    );

    const mappedClinics = clinics.map((clinic) => {
      return {
        id: clinic.ID,
        can_select_worker: !!clinic.OnlineCanSelectWorker,
        email: clinic.Email || null,
        name: clinic.Name,
        personal_code_required: !!clinic.PersonalCodeRequired,
        phone_number: clinic.Phone || null,
        key: clinic.Key,
        address: clinic.Address,
      };
    });

    const mappedServices = services.map((service) => {
      return {
        id: service.ID,
        clinic_id: service.ClinicID,
        sync_id: Number(service.SyncID),
        name: service.Name,
        description: service.Description || null,
        price: service.Price,
        requires_payment: !!service.RequiresPayment,
        duration: service.Duration,
        neto_duration: service.NetoDuration,
        display: service.Display,
        price_periods: service.PricePeriods || null,
        online_hide_duration: service.OnlineHideDuration,
        online_hide_price: service.OnlineHidePrice,
        code: service.Code,
        has_free_codes: !!service.HasFreeCodes,
      };
    });

    const mappedServiceProviders = serviceProviders.map((serviceProvider) => {
      const jobTitleTranslation = serviceProvider.JobTitleID
        ? jobTitleTranslations
            .get(serviceProvider.ClinicID)
            ?.get(serviceProvider.JobTitleID)
        : null;
      return {
        id: serviceProvider.ID,
        prefix: serviceProvider.Prefix,
        name: serviceProvider.Name,
        spoken_languages: getSpokenLanguages(serviceProvider.SpokenLanguages),
        job_title_et: jobTitleTranslation?.textET,
        job_title_en: jobTitleTranslation?.textEN,
        job_title_ru: jobTitleTranslation?.textRU,
        job_title_id: serviceProvider.JobTitleID,
        is_deleted: !!serviceProvider.Deleted,
        clinic_id: serviceProvider.ClinicID,
      };
    });

    const { error: providersError } = await supabase
      .schema('medreport')
      .from('connected_online_providers')
      .upsert(mappedClinics);

    if (providersError) {
      return logSyncResult({
        operation: 'CONNECTED_ONLINE_SYNC',
        comment:
          'Error saving connected online providers: ' +
          JSON.stringify(providersError),
        status: SyncStatus.Fail,
        changed_by_role: 'service_role',
      });
    }

    const { error: servicesError } = await supabase
      .schema('medreport')
      .from('connected_online_services')
      .upsert(mappedServices, {
        onConflict: 'id',
        ignoreDuplicates: false,
      });

    if (servicesError) {
      return logSyncResult({
        operation: 'CONNECTED_ONLINE_SYNC',
        comment:
          'Error saving connected online services: ' +
          JSON.stringify(servicesError),
        status: SyncStatus.Fail,
        changed_by_role: 'service_role',
      });
    }

    const { error: serviceProvidersError } = await supabase
      .schema('medreport')
      .from('connected_online_service_providers')
      .upsert(mappedServiceProviders, {
        onConflict: 'id',
        ignoreDuplicates: false,
      });

    if (serviceProvidersError) {
      return logSyncResult({
        operation: 'CONNECTED_ONLINE_SYNC',
        comment:
          'Error saving service providers: ' +
          JSON.stringify(serviceProvidersError),
        status: SyncStatus.Fail,
        changed_by_role: 'service_role',
      });
    }

    for (const mappedClinic of mappedClinics) {
      const defaultLoadResponse = await axios.post<{ d: string }>(
        `${baseUrl}/Default_Load`,
        {
          headers: {
            'Content-Type': 'application/json; charset=utf-8',
          },
          param: `{'Value':'${mappedClinic.key}|et'}`,
        },
      );

      const defaultLoadResponseData = JSON.parse(defaultLoadResponse.data.d);

      if (defaultLoadResponseData?.ErrorCode !== 0) {
        throw new Error('Failed to get Connected Online location data');
      }

      const clinicLocations: {
        SyncID: number;
        Address: string;
        Name: string;
      }[] = defaultLoadResponseData.Data.T_SelectableLocation;

      if (clinicLocations?.length) {
        const mappedLocations = clinicLocations.map(
          ({ SyncID, Address, Name }) => ({
            address: Address,
            clinic_id: mappedClinic.id,
            sync_id: SyncID,
            name: Name,
          }),
        );

        await supabase
          .schema('medreport')
          .from('connected_online_locations')
          .insert(mappedLocations)
          .throwOnError();
      }
    }

    await logSyncResult({
      operation: 'CONNECTED_ONLINE_SYNC',
      status: SyncStatus.Success,
      changed_by_role: 'service_role',
    });
  } catch (e) {
    await logSyncResult({
      operation: 'CONNECTED_ONLINE_SYNC',
      status: SyncStatus.Fail,
      comment: JSON.stringify(e),
      changed_by_role: 'service_role',
    });
    throw new Error(
      `Failed to sync Connected Online data, error: ${JSON.stringify(e)}`,
    );
  }
}
