'use server';

import {
  getAnalysisGroup,
  getClientInstitution,
  getClientPerson,
  getConfidentiality,
  getOrderEnteredPerson,
  getPais,
  getPatient,
  getProviderInstitution,
  getSpecimen,
} from '@/lib/templates/medipost-order';
import { MaterjalideGrupp } from '@/lib/types/medipost';
import { uniqBy } from 'lodash';

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

import { AnalysesWithGroupsAndElements } from '../analyses.service';
import { AnalysisElement } from '../analysis-element.service';

const USER = process.env.MEDIPOST_USER!;
const RECIPIENT = process.env.MEDIPOST_RECIPIENT!;

export type OrderedAnalysisElement = {
  analysisElementId?: number;
  analysisId?: number;
};

export async function composeOrderXML({
  analyses,
  analysisElements,
  person,
  orderId,
  orderCreatedAt,
  comment,
}: {
  analyses: AnalysesWithGroupsAndElements;
  analysisElements: AnalysisElement[];
  person: {
    idCode: string;
    firstName: string;
    lastName: string;
    phone: string;
  };
  orderId: number;
  orderCreatedAt: Date;
  comment?: string;
}) {
  const analysisGroups: Tables<{ schema: 'medreport' }, 'analysis_groups'>[] =
    uniqBy(
      (
        analysisElements?.flatMap(({ analysis_groups }) => analysis_groups) ??
        []
      ).concat(
        analyses?.flatMap(
          ({ analysis_elements }) => analysis_elements.analysis_groups,
        ) ?? [],
      ),
      'id',
    );

  // First, collect all unique materials across all analysis groups
  const uniqueMaterials = new Map<
    string,
    {
      MaterjaliTyypOID: string;
      MaterjaliTyyp: string;
      MaterjaliNimi: string;
      ProovinouKoodOID?: string;
      ProovinouKood?: string;
      order: number;
    }
  >();

  let specimenOrder = 1;

  // Collect all materials from all analysis groups
  for (const currentGroup of analysisGroups) {
    let relatedAnalysisElements = analysisElements?.filter(
      ({ analysis_groups }) => analysis_groups.id === currentGroup.id,
    );

    if (!relatedAnalysisElements || relatedAnalysisElements.length === 0) {
      relatedAnalysisElements = analyses
        .filter(
          ({ analysis_elements }) =>
            analysis_elements.analysis_groups.id === currentGroup.id,
        )
        .flatMap(({ analysis_elements }) => analysis_elements);
    }

    if (!relatedAnalysisElements || relatedAnalysisElements.length === 0) {
      throw new Error(
        `Failed to find related analysis elements for group ${currentGroup.name} (id: ${currentGroup.id})`,
      );
    }

    for (const analysisElement of relatedAnalysisElements) {
      for (const {
        Materjal,
      } of analysisElement.material_groups as MaterjalideGrupp[]) {
        for (const material of toArray(Materjal)) {
          const { MaterjaliTyyp } = material;

          for (const container of toArray(material.Konteiner)) {
            if (uniqueMaterials.has(MaterjaliTyyp)) {
              continue;
            }

            uniqueMaterials.set(MaterjaliTyyp, {
              MaterjaliTyypOID: material.MaterjaliTyypOID,
              MaterjaliTyyp: material.MaterjaliTyyp,
              MaterjaliNimi: material.MaterjaliNimi,
              ProovinouKoodOID: container.ProovinouKoodOID,
              ProovinouKood: container.ProovinouKood,
              order: specimenOrder++,
            });
          }
        }
      }
    }
  }

  // Generate specimen section from unique materials
  const specimenSection = Array.from(uniqueMaterials.values()).map((material) =>
    getSpecimen(
      material.MaterjaliTyypOID,
      material.MaterjaliTyyp,
      material.MaterjaliNimi,
      material.order,
      material.ProovinouKoodOID,
      material.ProovinouKood,
    ),
  );

  // Generate analysis section with correct specimen references
  const analysisSection = [];
  for (const currentGroup of analysisGroups) {
    let relatedAnalysisElements = analysisElements?.filter(
      (element) => element.analysis_groups.id === currentGroup.id,
    );

    if (!relatedAnalysisElements) {
      relatedAnalysisElements = analyses
        .filter(
          ({ analysis_elements }) =>
            analysis_elements.analysis_groups.id === currentGroup.id,
        )
        .flatMap(({ analysis_elements }) => analysis_elements);
    }

    if (!relatedAnalysisElements || relatedAnalysisElements.length === 0) {
      throw new Error(
        `Failed to find related analysis element for group ${currentGroup.name} (id: ${currentGroup.id})`,
      );
    }

    const uuringElementInputs: {
      analysisElement: Tables<{ schema: 'medreport' }, 'analysis_elements'>;
      specimenOrderNr: number;
    }[] = [];
    for (const analysisElement of relatedAnalysisElements) {
      for (const group of analysisElement.material_groups as MaterjalideGrupp[]) {
        const materials = toArray(group.Materjal);
        for (const material of materials) {
          const uniqueMaterial = uniqueMaterials.get(material.MaterjaliTyyp);
          if (!uniqueMaterial) {
            console.info(
              `Unique material not found for material: ${material.MaterjaliTyyp}, analysis element: ${analysisElement.id} ${analysisElement.analysis_id_original} ${analysisElement.analysis_name_lab}`,
            );
            continue;
          }
          uuringElementInputs.push({
            analysisElement,
            specimenOrderNr: uniqueMaterial.order,
          });
          break;
        }
      }
    }

    const groupXml = getAnalysisGroup(
      currentGroup.original_id,
      currentGroup.name,
      uuringElementInputs,
    );
    analysisSection.push(groupXml);
  }

  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, 'OL')}
  <Tellimus cito="EI">
		<ValisTellimuseId>${orderId}</ValisTellimuseId>
	  ${getClientInstitution()}
		${getProviderInstitution()}
    ${getClientPerson()}
    ${getOrderEnteredPerson()}
		<TellijaMarkused>${comment ?? ''}</TellijaMarkused>
    ${getPatient(person)}
		${getConfidentiality()}
    ${specimenSection.join('')}
    ${analysisSection?.join('')}
	</Tellimus>
</Saadetis>`;
}
