'use server';

import {
  getClientInstitution,
  getClientPerson,
  getOrderEnteredPerson,
  getPais,
  getPatient,
  getProviderInstitution,
} from '@/lib/templates/medipost-order';
import { MedipostAction } from '@/lib/types/medipost';
import axios from 'axios';
import { formatDate } from 'date-fns';
import { uniqBy } from 'lodash';

import { Tables } from '@kit/supabase/database';

import { getAnalyses } from '../analyses.service';
import { getAnalysisElementsAdmin } from '../analysis-element.service';
import { validateMedipostResponse } from './medipostValidate.service';

const BASE_URL = process.env.MEDIPOST_URL!;
const USER = process.env.MEDIPOST_USER!;
const PASSWORD = process.env.MEDIPOST_PASSWORD!;
const RECIPIENT = process.env.MEDIPOST_RECIPIENT!;

export async function sendPrivateMessageTestResponse({
  messageXml,
}: {
  messageXml: string;
}) {
  const body = new FormData();
  body.append('Action', MedipostAction.SendPrivateMessage);
  body.append('User', USER);
  body.append('Password', PASSWORD);
  body.append('Receiver', RECIPIENT);
  body.append('MessageType', 'Vastus');
  body.append(
    'Message',
    new Blob([messageXml], {
      type: 'text/xml; charset=UTF-8',
    }),
  );

  const { data } = await axios.post(BASE_URL, body);
  await validateMedipostResponse(data);
}

function getRandomInt(min: number, max: number) {
  min = Math.ceil(min);
  max = Math.floor(max);
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

export async function composeOrderTestResponseXML({
  person,
  orderedAnalysisElementsIds,
  orderedAnalysesIds,
  orderId,
  orderCreatedAt,
}: {
  person: {
    idCode: string;
    firstName: string;
    lastName: string;
    phone: string;
  };
  orderedAnalysisElementsIds: number[];
  orderedAnalysesIds: number[];
  orderId: number;
  orderCreatedAt: Date;
}) {
  const analysisElements = await getAnalysisElementsAdmin({
    ids: orderedAnalysisElementsIds,
  });
  const analyses = await getAnalyses({ ids: orderedAnalysesIds });

  const analysisGroups: Tables<{ schema: 'medreport' }, 'analysis_groups'>[] =
    uniqBy(
      (
        analysisElements?.flatMap(({ analysis_groups }) => analysis_groups) ??
        []
      ).concat(
        analyses?.flatMap(
          ({ analysis_elements }) => analysis_elements.analysis_groups,
        ) ?? [],
      ),
      'id',
    );

  // Tellimuse olek:
  // 1 – Järjekorras, 2 – Ootel, 3 - Töös, 4 – Lõpetatud,
  // 5 – Tagasi lükatud, 6 – Tühistatud.
  const orderStatus = 4;
  const orderNumber = orderId;

  const allAnalysisElementsForGroups = analysisElements?.filter((element) => {
    return analysisGroups.some(
      (group) => group.id === element.analysis_groups.id,
    );
  });
  const addedIds = new Set<number>();

  return `<?xml version="1.0" encoding="UTF-8"?>
  <Saadetis xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="TellimusLOINC.xsd">
  ${getPais(USER, RECIPIENT, orderId, 'AL')}
  <Vastus>
		<ValisTellimuseId>${orderId}</ValisTellimuseId>
	    ${getClientInstitution({ index: 1 })}
		  ${getProviderInstitution({ index: 1 })}
      ${getClientPerson()}
      ${getOrderEnteredPerson()}
		<TellijaMarkused>Siia tuleb tellija poolne märkus</TellijaMarkused>

		${getPatient(person)}

    <Proov>
			<ProovinouIdOID>1.3.6.1.4.1.28284.1.625.2.17</ProovinouIdOID>
			<ProovinouId>16522314</ProovinouId>
			<MaterjaliTyypOID>1.3.6.1.4.1.28284.6.2.1.244.8</MaterjaliTyypOID>
			<MaterjaliTyyp>119297000</MaterjaliTyyp>
			<MaterjaliNimi>Veri</MaterjaliNimi>
			<Ribakood>16522314</Ribakood>
			<Jarjenumber>1</Jarjenumber>
			<VotmisAeg>2022-08-19 08:53:00</VotmisAeg>
			<SaabumisAeg>2022-08-23 15:10:00</SaabumisAeg>
		</Proov>

    <TellimuseNumber>${orderNumber}</TellimuseNumber>

		<TellimuseOlek>${orderStatus}</TellimuseOlek>
      ${allAnalysisElementsForGroups
        .map((analysisElement) => {
          const group = analysisGroups.find(
            (group) => group.id === analysisElement.analysis_groups.id,
          );
          if (!group) {
            throw new Error(
              `Failed to find group for analysis element ${analysisElement.id}`,
            );
          }

          let relatedAnalysisElement = analysisElements?.find(
            (element) =>
              element.analysis_groups.id === group.id &&
              !addedIds.has(element.id),
          );
          const relatedAnalyses = analyses?.filter((analysis) => {
            return (
              analysis.analysis_elements.analysis_groups.id === group.id &&
              !addedIds.has(analysis.analysis_elements.id)
            );
          });

          if (!relatedAnalysisElement) {
            relatedAnalysisElement = relatedAnalyses?.find(
              (relatedAnalysis) =>
                relatedAnalysis.analysis_elements.analysis_groups.id ===
                group.id,
            )?.analysis_elements;
          }

          if (
            !relatedAnalysisElement ||
            !relatedAnalysisElement.material_groups
          ) {
            throw new Error(
              `Failed to find related analysis element for group ${group.name} (id: ${group.id})`,
            );
          }

          const lower = getRandomInt(0, 100);
          const upper = getRandomInt(lower + 1, 500);
          const result = getRandomInt(
            lower - Math.floor(lower * 0.1),
            upper + Math.floor(upper * 0.1),
          );
          addedIds.add(relatedAnalysisElement.id);
          return `
          <UuringuGrupp>
            <UuringuGruppId>${group.original_id}</UuringuGruppId>
            <UuringuGruppNimi>${group.name}</UuringuGruppNimi>
            <Uuring>
              <UuringuElement>
                <UuringIdOID>${relatedAnalysisElement.analysis_id_oid}</UuringIdOID>
                <UuringId>${relatedAnalysisElement.analysis_id_original}</UuringId>
                <TLyhend>${relatedAnalysisElement.tehik_short_loinc}</TLyhend>
                <KNimetus>${relatedAnalysisElement.tehik_loinc_name}</KNimetus>
                <UuringNimi>${relatedAnalysisElement.analysis_name_lab ?? relatedAnalysisElement.tehik_loinc_name}</UuringNimi>
                <TellijaUuringId>${relatedAnalysisElement.id}</TellijaUuringId>
                <TeostajaUuringId>${relatedAnalysisElement.id}</TeostajaUuringId>
                <UuringOlek>4</UuringOlek>
                <Mootyhik>%</Mootyhik>
                <UuringuVastus>
                  <VastuseVaartus>${result}</VastuseVaartus>
                  <VastuseAeg>${formatDate(new Date(), 'yyyy-MM-dd HH:mm:ss')}</VastuseAeg>
                  <NormYlem kaasaarvatud=\"EI\">${upper}</NormYlem>
                  <NormAlum kaasaarvatud=\"EI\">${lower}</NormAlum>
                  <NormiStaatus>${result < lower ? 1 : result > upper ? 1 : 0}</NormiStaatus>
                  <ProoviJarjenumber>1</ProoviJarjenumber>
                </UuringuVastus>
              </UuringuElement>
              <UuringuTaitjaAsutuseJnr>2</UuringuTaitjaAsutuseJnr>
            </Uuring>
          </UuringuGrupp>
        `;
        })
        .join('')}
	</Vastus>
</Saadetis>`;
}
