import { t } from '@lingui/core/macro';
import { ActionIcon, Badge, Group, Text, Tooltip } from '@mantine/core';
import { IconCirclePlus } from '@tabler/icons-react';
import { useQuery } from '@tanstack/react-query';
import {
  type ReactNode,
  useCallback,
  useEffect,
  useMemo,
  useState
} from 'react';

import { PassFailButton } from '@lib/components/YesNoButton';
import { ApiEndpoints } from '@lib/enums/ApiEndpoints';
import { ModelType } from '@lib/enums/ModelType';
import { apiUrl } from '@lib/functions/Api';
import { cancelEvent } from '@lib/functions/Events';
import useTable from '@lib/hooks/UseTable';
import { AddItemButton } from '@lib/index';
import type { TableFilter } from '@lib/types/Filters';
import type { ApiFormFieldSet } from '@lib/types/Forms';
import type { TableColumn } from '@lib/types/Tables';
import type { UseFormReturn } from 'react-hook-form';
import { RenderUser } from '../../components/render/User';
import { useApi } from '../../contexts/ApiContext';
import { formatDate } from '../../defaults/formatters';
import { useTestResultFields } from '../../forms/StockForms';
import { useCreateApiFormModal } from '../../hooks/UseForm';
import { LocationColumn, PartColumn } from '../ColumnRenderers';
import {
  BatchFilter,
  HasBatchCodeFilter,
  InStockFilter,
  IsSerializedFilter,
  SerialFilter,
  SerialGTEFilter,
  SerialLTEFilter,
  StatusFilterOptions
} from '../Filter';
import { InvenTreeTable } from '../InvenTreeTable';
import { TableHoverCard } from '../TableHoverCard';

/**
 * A table which displays all "test results" for the outputs generated by a build order.
 */
export default function PartTestResultTable({
  buildId,
  partId
}: Readonly<{
  buildId?: number;
  partId: number;
}>) {
  const tableName = useMemo(() => {
    if (buildId) {
      return 'build-test-results';
    } else {
      return 'part-test-results';
    }
  }, [buildId]);

  const table = useTable(tableName);
  const api = useApi();

  // Fetch the test templates required for this build order
  const { data: testTemplates } = useQuery({
    queryKey: ['build-test-templates', partId, buildId],
    queryFn: async () => {
      if (!partId) {
        return [];
      }

      return api
        .get(apiUrl(ApiEndpoints.part_test_template_list), {
          params: {
            part: partId,
            include_inherited: true,
            enabled: true,
            required: true
          }
        })
        .then((res) => res.data);
    }
  });

  // Reload the table data whenever the set of templates changes
  useEffect(() => {
    table.refreshTable();
  }, [testTemplates]);

  const [selectedOutput, setSelectedOutput] = useState<number>(0);
  const [selectedTemplate, setSelectedTemplate] = useState<number | undefined>(
    undefined
  );

  const testResultFields: ApiFormFieldSet = useTestResultFields({
    partId: partId,
    itemId: selectedOutput,
    templateId: selectedTemplate
  });

  const createTestResult = useCreateApiFormModal({
    url: apiUrl(ApiEndpoints.stock_test_result_list),
    title: t`Add Test Result`,
    fields: testResultFields,
    initialData: {
      template: selectedTemplate,
      result: true
    },
    onFormSuccess: () => table.refreshTable(),
    successMessage: t`Test result added`
  });

  const multipleTestResultFields: ApiFormFieldSet = useMemo(() => {
    const fields: ApiFormFieldSet = { ...testResultFields };

    // Do not allow attachment for multiple test results
    delete fields.attachment;
    delete fields.stock_item;

    fields.template.disabled = false;

    return fields;
  }, [partId, testResultFields]);

  const generateTestResults = useCallback(
    (data: any, form: UseFormReturn) => {
      // Generate a list of test results for each selected output
      const results = table.selectedRecords.map((record: any) => {
        return {
          ...data,
          stock_item: record.pk
        };
      });

      return results;
    },
    [table.selectedIds]
  );

  const createTestResultMultiple = useCreateApiFormModal({
    url: apiUrl(ApiEndpoints.stock_test_result_list),
    title: t`Add Test Results`,
    fields: multipleTestResultFields,
    initialData: {
      result: true
    },
    onFormSuccess: () => {
      table.clearSelectedRecords();
      table.refreshTable();
    },
    processFormData: generateTestResults,
    successMessage: t`Test results added`
  });

  // Generate a table column for each test template
  const testColumns: TableColumn[] = useMemo(() => {
    if (!testTemplates || testTemplates.length == 0) {
      return [];
    }

    return testTemplates.map((template: any) => {
      return {
        accessor: `test_${template.pk}`,
        title: template.test_name,
        sortable: false,
        switchable: true,
        render: (record: any) => {
          const tests = record.tests || [];

          // Find the most recent test result (highest primary key)
          const test = tests
            .filter((test: any) => test.template == template.pk)
            .sort((a: any, b: any) => b.pk - a.pk)
            .shift();

          // No test result recorded
          if (!test || test.result === undefined) {
            return (
              <Group gap='xs' wrap='nowrap' justify='space-between'>
                <Badge color='lightblue' variant='filled'>{t`No Result`}</Badge>
                <Tooltip label={t`Add Test Result`}>
                  <ActionIcon
                    size='lg'
                    color='green'
                    aria-label='add-test-result'
                    variant='transparent'
                    onClick={(event: any) => {
                      cancelEvent(event);
                      setSelectedOutput(record.pk);
                      setSelectedTemplate(template.pk);
                      createTestResult.open();
                    }}
                  >
                    <IconCirclePlus />
                  </ActionIcon>
                </Tooltip>
              </Group>
            );
          }

          const extra: ReactNode[] = [];

          if (test.value) {
            extra.push(
              <Text key='value' size='sm'>
                {t`Value`}: {test.value}
              </Text>
            );
          }

          if (test.notes) {
            extra.push(
              <Text key='notes' size='sm'>
                {t`Notes`}: {test.notes}
              </Text>
            );
          }

          if (test.date) {
            extra.push(
              <Text key='date' size='sm'>
                {t`Date`}: {formatDate(test.date)}
              </Text>
            );
          }

          if (test.user_detail) {
            extra.push(<RenderUser key='user' instance={test.user_detail} />);
          }

          return (
            <TableHoverCard
              value={<PassFailButton value={test.result} />}
              title={template.test_name}
              extra={extra}
            />
          );
        }
      };
    });
  }, [testTemplates]);

  const tableColumns: TableColumn[] = useMemo(() => {
    // Fixed columns
    const columns: TableColumn[] = [
      PartColumn({
        title: t`Part`,
        part: 'part_detail',
        full_name: true,
        ordering: 'part',
        sortable: true,
        switchable: true
      }),
      {
        accessor: 'stock',
        title: t`Stock Item`,
        sortable: true,
        switchable: false,
        render: (record: any) => {
          if (record.serial) {
            return `# ${record.serial}`;
          } else {
            const extra: ReactNode[] = [];

            if (record.batch) {
              extra.push(
                <Text key='batch' size='sm'>
                  {t`Batch Code`}: {record.batch}
                </Text>
              );
            }

            return (
              <TableHoverCard
                value={
                  <Text>
                    {t`Quantity`}: {record.quantity}
                  </Text>
                }
                title={t`Build Output`}
                extra={extra}
              />
            );
          }
        }
      },
      {
        accessor: 'batch',
        title: t`Batch Code`,
        sortable: true,
        switchable: true,
        copyable: true
      },
      LocationColumn({
        accessor: 'location_detail'
      })
    ];

    return [...columns, ...testColumns];
  }, [testColumns]);

  const tableFilters: TableFilter[] = useMemo(() => {
    return [
      {
        name: 'is_building',
        label: t`In Production`,
        description: t`Show build outputs currently in production`
      },
      InStockFilter(),
      SerialFilter(),
      SerialGTEFilter(),
      SerialLTEFilter(),
      HasBatchCodeFilter(),
      BatchFilter(),
      IsSerializedFilter(),
      {
        name: 'status',
        label: t`Status`,
        description: t`Filter by stock status`,
        choiceFunction: StatusFilterOptions(ModelType.stockitem)
      }
    ];
  }, []);

  const tableActions = useMemo(() => {
    return [
      <AddItemButton
        key='add-test-result'
        tooltip={t`Add Test Result`}
        disabled={!table.hasSelectedRecords}
        onClick={(event: any) => {
          createTestResultMultiple.open();
        }}
      />
    ];
  }, [table.hasSelectedRecords]);

  const rowActions = useCallback((record: any) => {
    return [
      {
        icon: <IconCirclePlus />,
        color: 'green',
        title: t`Add Test Result`,
        onClick: (event: any) => {
          setSelectedOutput(record.pk);
          setSelectedTemplate(undefined);
          createTestResult.open();
        }
      }
    ];
  }, []);

  return (
    <>
      {createTestResult.modal}
      {createTestResultMultiple.modal}
      <InvenTreeTable
        url={apiUrl(ApiEndpoints.stock_item_list)}
        tableState={table}
        columns={tableColumns}
        props={{
          params: {
            part_detail: true,
            location_detail: true,
            tests: true,
            part: partId,
            build: buildId
          },
          enableSelection: true,
          rowActions: rowActions,
          tableFilters: tableFilters,
          tableActions: tableActions,
          modelType: ModelType.stockitem
        }}
      />
    </>
  );
}
