import { listProductTypes } from '@lib/data/products';
import Medusa from '@medusajs/js-sdk';
import type { AdminProductCategory } from '@medusajs/types';

import {
  createMedusaSyncFailEntry,
  createMedusaSyncSuccessEntry,
} from '~/lib/services/analyses.service';
import { getAnalysisElements } from '~/lib/services/analysis-element.service';
import { getAnalysisGroups } from '~/lib/services/analysis-group.service';

const SYNLAB_SERVICES_CATEGORY_HANDLE = 'synlab-services';
const SYNLAB_ANALYSIS_PRODUCT_TYPE_HANDLE = 'synlab-analysis';

const BASE_ANALYSIS_PRODUCT_HANDLE = 'analysis-base';

const getAdminSdk = () => {
  const medusaBackendUrl = process.env.MEDUSA_BACKEND_PUBLIC_URL!;
  const medusaPublishableApiKey =
    process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY!;
  const key = process.env.MEDUSA_SECRET_API_KEY!;

  if (!medusaBackendUrl || !medusaPublishableApiKey) {
    throw new Error('Medusa environment variables not set');
  }
  return new Medusa({
    baseUrl: medusaBackendUrl,
    debug: process.env.NODE_ENV === 'development',
    apiKey: key,
  });
};

async function createProductCategories({ medusa }: { medusa: Medusa }) {
  const { product_categories: existingProductCategories } =
    await medusa.admin.productCategory.list();
  const parentCategory = existingProductCategories.find(
    ({ handle }) => handle === SYNLAB_SERVICES_CATEGORY_HANDLE,
  );

  if (!parentCategory) {
    throw new Error('Parent category not found');
  }

  const analysisGroups = await getAnalysisGroups();
  if (!analysisGroups) {
    throw new Error('Analysis groups not found');
  }

  const createdCategories: AdminProductCategory[] = [];
  for (const analysisGroup of analysisGroups) {
    console.info(`Processing analysis group '${analysisGroup.name}'`);

    const isExisting = existingProductCategories.find(
      ({ name }) => name === analysisGroup.name,
    );
    const isNewlyCreated = createdCategories.find(
      ({ name }) => name === analysisGroup.name,
    );
    if (isExisting || isNewlyCreated) {
      console.info(`Analysis group '${analysisGroup.name}' already exists`);
      continue;
    }

    const createResponse = await medusa.admin.productCategory.create({
      name: analysisGroup.name,
      handle: analysisGroup.name,
      parent_category_id: parentCategory.id,
      is_active: true,
      metadata: {
        analysisGroupOriginalId: analysisGroup.original_id,
        analysisGroupId: analysisGroup.id,
      },
    });
    console.info(
      `Successfully created category, id=${createResponse.product_category.id}`,
    );
    createdCategories.push(createResponse.product_category);
  }
}

async function getChildProductCategories({ medusa }: { medusa: Medusa }) {
  const { product_categories: allCategories } =
    await medusa.admin.productCategory.list();
  const childCategories = allCategories.filter(
    ({ parent_category_id }) => parent_category_id !== null,
  );
  return childCategories;
}

async function deleteProductCategories({
  medusa,
  categories,
}: {
  medusa: Medusa;
  categories: AdminProductCategory[];
}) {
  for (const category of categories) {
    await medusa.admin.productCategory.delete(category.id);
  }
}

/**
 * In case a reset is needed
 */
async function deleteProducts({ medusa }: { medusa: Medusa }) {
  const { products: existingProducts } = await medusa.admin.product.list({
    fields: 'id,collection_id',
    limit: 1000,
  });

  await Promise.all(
    existingProducts
      .filter((a) => !a.collection_id)
      .map(({ id }) => medusa.admin.product.delete(id)),
  );
}

async function getAnalysisPackagesType() {
  const { productTypes } = await listProductTypes();
  const analysisPackagesType = productTypes.find(
    ({ metadata }) => metadata?.handle === SYNLAB_ANALYSIS_PRODUCT_TYPE_HANDLE,
  );
  if (!analysisPackagesType) {
    throw new Error('Synlab analysis packages type not found');
  }
  return analysisPackagesType;
}

async function getProductDefaultFields({ medusa }: { medusa: Medusa }) {
  const baseProductsResponse = await medusa.admin.product.list({
    handle: BASE_ANALYSIS_PRODUCT_HANDLE,
  });
  const baseProduct = baseProductsResponse.products[0];
  if (!baseProduct) {
    throw new Error('Base product not found');
  }
  const defaultSalesChannels = baseProduct.sales_channels;
  if (!Array.isArray(defaultSalesChannels)) {
    throw new Error('Base analysis product has no required sales channels');
  }
  const defaultProductOption = baseProduct.options;
  if (!Array.isArray(defaultProductOption)) {
    throw new Error('Base analysis product has no required options');
  }
  const defaultProductVariant = baseProduct.variants?.[0];
  if (!defaultProductVariant) {
    throw new Error('Base analysis product has no required variant');
  }

  return {
    defaultSalesChannels,
    defaultProductOption,
    defaultProductVariant,
  };
}

async function createProducts({ medusa }: { medusa: Medusa }) {
  const { product_categories: allCategories } =
    await medusa.admin.productCategory.list();

  const [
    { products: existingProducts },
    analysisElements,
    analysisPackagesType,
    { defaultSalesChannels, defaultProductOption, defaultProductVariant },
  ] = await Promise.all([
    medusa.admin.product.list({
      category_id: allCategories.map(({ id }) => id),
    }),
    getAnalysisElements({ getAll: true }),
    getAnalysisPackagesType(),
    getProductDefaultFields({ medusa }),
  ]);

  for (const analysisElement of analysisElements) {
    const { analysis_id_original: originalId } = analysisElement;
    const isExisting = existingProducts.find(
      ({ metadata }) => metadata?.analysisIdOriginal === originalId,
    );
    if (isExisting) {
      console.info(
        `Analysis element '${analysisElement.analysis_name_lab}' already exists`,
      );
      continue;
    }
    const { analysis_name_lab: name } = analysisElement;
    if (!name) {
      console.error(`Analysis element '${originalId}' has no name`);
      continue;
    }

    const category = allCategories.find(
      ({ metadata }) =>
        metadata?.analysisGroupId === analysisElement.parent_analysis_group_id,
    );
    if (!category) {
      console.error(`Category not found for analysis element '${name}'`);
      continue;
    }

    await medusa.admin.product.create({
      title: name,
      handle: `analysis-element-${analysisElement.id}`,
      categories: [{ id: category.id }],
      options: defaultProductOption.map(({ id, title, values }) => ({
        id,
        title,
        values: values?.map(({ value }) => value) ?? [],
      })),
      metadata: {
        analysisIdOriginal: originalId,
      },
      is_giftcard: false,
      discountable: false,
      status: 'published',
      sales_channels: defaultSalesChannels.map(({ id }) => ({ id })),
      variants: [
        {
          title: defaultProductVariant.title!,
          prices: defaultProductVariant.prices!,
          manage_inventory: false,
        },
      ],
      type_id: analysisPackagesType.id,
    });
  }
}

export default async function syncAnalysisGroupsStore() {
  const medusa = getAdminSdk();

  try {
    await createProductCategories({ medusa });

    // const categories = await getChildProductCategories({ medusa });
    // await deleteProductCategories({ medusa, categories });
    // await deleteProducts({ medusa });
    // return;

    await createProducts({ medusa });

    await createMedusaSyncSuccessEntry();
  } catch (e) {
    await createMedusaSyncFailEntry(JSON.stringify(e));
    console.error(e);
    throw new Error(
      `Failed to sync analyses to Medusa, error: ${JSON.stringify(e)}`,
    );
  }
}
