import { useEffect, useState } from 'react';
import { omit } from 'lodash';
import {
  Button,
  Col,
  Popconfirm,
  Row,
  Space,
  Spin,
  Typography,
  Alert,
} from 'antd';
import type { ColumnsType } from 'antd/es/table';
import DeleteOutlined from '@ant-design/icons/DeleteOutlined';
import EditOutlined from '@ant-design/icons/EditOutlined';
import PlusOutlined from '@ant-design/icons/PlusOutlined';
import ModelRelationSelectionTable, {
  RelationsDataType,
} from '@/components/table/ModelRelationSelectionTable';
import { getJoinTypeText } from '@/utils/data';
import useModalAction from '@/hooks/useModalAction';
import RelationModal, {
  RelationFieldValue,
  RelationFormValues,
} from '@/components/modals/RelationModal';
import { convertFormValuesToIdentifier } from '@/hooks/useCombineFieldOptions';

const { Title, Text } = Typography;

// for omit keys
const relationKeys = ['name', 'isAutoGenerated'];

export interface SelectedRecommendRelations {
  [modelName: string]: RelationsDataType[];
}

interface Props {
  fetching: boolean;
  recommendRelations: SelectedRecommendRelations;
  recommendNameMapping: Record<string, string>;
  onNext: (data: { relations: SelectedRecommendRelations }) => void;
  onBack: () => void;
  onSkip: () => void;
  submitting: boolean;
}

interface EditableRelationTableProps {
  index: number;
  modelName: string;
  onSetRelation: (payload: {
    modelName: string;
    defaultValue?: RelationsDataType;
  }) => void;
  onDeleteRow: (modelName: string, selectedRelation: RelationsDataType) => void;
  relations: RelationsDataType[];
  recommendNameMapping: Record<string, string>;
}

function EditableRelationTable(props: EditableRelationTableProps) {
  const {
    index,
    modelName,
    onSetRelation,
    onDeleteRow,
    relations,
    recommendNameMapping,
  } = props;

  const columns: ColumnsType<RelationsDataType> = [
    {
      title: 'From',
      dataIndex: 'fromField',
      key: 'fromField',
      render: (fromField) => `${fromField.modelName}.${fromField.fieldName}`,
      width: '35%',
    },
    {
      title: 'To',
      dataIndex: 'toField',
      key: 'toField',
      render: (toField) => `${toField.modelName}.${toField.fieldName}`,
      width: '35%',
    },
    {
      title: 'Type',
      dataIndex: 'type',
      key: 'type',
      render: (type, relation) => (
        <>
          {getJoinTypeText(type)}
          {relation.isAutoGenerated && (
            <Text className="pl-1" type="secondary">
              (auto-generated)
            </Text>
          )}
        </>
      ),
      width: '30%',
    },
    {
      title: '',
      key: 'action',
      width: 48,
      align: 'center',
      render: (_, record) => (
        <Space size={[16, 0]}>
          <EditOutlined
            onClick={() =>
              onSetRelation({
                modelName,
                defaultValue: record,
              })
            }
          />
          <Popconfirm
            title="Confirm to delete?"
            okText="Delete"
            okButtonProps={{ danger: true }}
            onConfirm={() => onDeleteRow(modelName, record)}
          >
            <DeleteOutlined />
          </Popconfirm>
        </Space>
      ),
    },
  ];

  return (
    <div className="mt-6">
      <ModelRelationSelectionTable
        columns={columns}
        dataSource={relations}
        tableTitle={recommendNameMapping[modelName]}
        extra={(onCollapseOpen) => (
          <Button
            onClick={(event) => {
              onSetRelation({ modelName });
              onCollapseOpen(event, recommendNameMapping[modelName]);
            }}
            size="small"
            title="Add relationship"
          >
            <PlusOutlined />
            Add
          </Button>
        )}
        rowKey={(record: RelationsDataType) =>
          `${modelName}-${record.fromField.fieldName}-${record.toField.modelName}-${record.toField.fieldName}-${index}`
        }
      />
    </div>
  );
}

export default function DefineRelations(props: Props) {
  const {
    fetching,
    recommendRelations,
    recommendNameMapping,
    onBack,
    onNext,
    onSkip,
    submitting,
  } = props;

  const [relations, setRelations] =
    useState<SelectedRecommendRelations>(recommendRelations);

  const [selectedRelation, setSelectedRelation] = useState<{
    modelName: string;
    defaultValue?: RelationsDataType;
  }>(null);

  const [showNoRecommendationAlert, setShowNoRecommendationAlert] =
    useState<boolean>(false);

  useEffect(() => {
    setRelations(recommendRelations);

    const recommendRelationsValues = Object.values(recommendRelations);
    if (recommendRelationsValues.length === 0) return;

    const allEmpty = recommendRelationsValues.every(
      (value) => value.length === 0,
    );
    setShowNoRecommendationAlert(allEmpty);
  }, [recommendRelations]);

  const relationModal = useModalAction();

  // check is the relation is auto-generated or not
  const isRecommendRelation = (
    modelName: string,
    relation: RelationsDataType,
  ) => {
    const isOriginalRelation = (recommendRelations[modelName] || []).find(
      (originalRelation) =>
        JSON.stringify(omit(originalRelation, relationKeys)) ===
        JSON.stringify(omit(relation, relationKeys)),
    );

    return isOriginalRelation?.isAutoGenerated || false;
  };

  const onAddRelation = (relationFormValues: RelationFormValues) => {
    const relation = convertFormValuesToIdentifier(relationFormValues);
    const modelName = relation.fromField.modelName;
    const isAutoGenerated = isRecommendRelation(modelName, relation);
    const newRelations = {
      ...relations,
      [modelName]: [
        ...(relations[modelName] || []),
        { ...relation, isAutoGenerated },
      ],
    };
    setRelations(newRelations);
  };

  const onCloseModal = () => {
    setSelectedRelation(null);
    relationModal.closeModal();
  };

  const onDeleteRow = (
    modelName: string,
    selectedRelation: RelationsDataType,
  ) => {
    const newRelations = {
      ...relations,
      [modelName]: relations[modelName].filter(
        (relation) =>
          JSON.stringify(relation) !== JSON.stringify(selectedRelation),
      ),
    };
    setRelations(newRelations);
  };

  const onSetRelation = (payload: {
    modelName: string;
    defaultValue?: RelationsDataType;
  }) => {
    setSelectedRelation(payload);
    relationModal.openModal();
  };

  const onUpdateRelation = (
    modelName: string,
    originalRelationValue: RelationsDataType,
    newRelationValue: RelationFormValues,
  ) => {
    const newRelation = convertFormValuesToIdentifier(newRelationValue);
    const isAutoGenerated = isRecommendRelation(modelName, newRelation);
    const newRelations = {
      ...relations,
      [modelName]: relations[modelName].map((relation) => {
        if (
          JSON.stringify(relation) === JSON.stringify(originalRelationValue)
        ) {
          return { ...newRelation, isAutoGenerated };
        }
        return relation;
      }),
    };

    setRelations(newRelations);
  };

  const submit = () => {
    onNext && onNext({ relations });
  };

  return (
    <div>
      <Title level={1} className="mb-3">
        Define relationships
      </Title>
      <Text>
        You can create relationships between selected tables. We provide
        suggested relationships based on primary and foreign keys defined in
        your data source. The relationships are then added to data models.
      </Text>
      {showNoRecommendationAlert && (
        <Alert
          message="No recommended relationships"
          description="No relationships are recommended because no primary or foreign keys were detected."
          type="info"
          showIcon
          className="my-6"
        />
      )}
      <div className="my-6 text-center">
        {Object.entries(relations).map(
          ([modelReferenceName, relations = []], index) => (
            <EditableRelationTable
              key={`${modelReferenceName}-${relations.length}`}
              index={index}
              modelName={modelReferenceName}
              relations={relations}
              onSetRelation={onSetRelation}
              onDeleteRow={onDeleteRow}
              recommendNameMapping={recommendNameMapping}
            />
          ),
        )}
        <Spin spinning={fetching} tip="Loading..." className="my-15" />
      </div>
      <Row gutter={16} className="pt-6">
        <Col span={12}>
          <Button onClick={onBack} size="large" className="adm-onboarding-btn">
            Back
          </Button>
        </Col>
        <Col className="text-right" span={12}>
          <Button
            className="mr-4 gray-7 adm-onboarding-btn"
            type="text"
            size="large"
            onClick={onSkip}
            disabled={submitting}
            data-ph-capture="true"
            data-ph-capture-attribute-name="cta_skip_define_relationship"
          >
            Skip this step
          </Button>
          <Button
            type="primary"
            size="large"
            onClick={submit}
            className="adm-onboarding-btn"
            loading={submitting}
            disabled={fetching}
            data-ph-capture="true"
            data-ph-capture-attribute-name="cta_finish_define_relationship"
          >
            Finish
          </Button>
        </Col>
      </Row>
      <RelationModal
        {...relationModal.state}
        model={selectedRelation?.modelName}
        onSubmit={async (values) => {
          if (selectedRelation?.defaultValue) {
            onUpdateRelation(
              selectedRelation.modelName,
              selectedRelation.defaultValue,
              values,
            );
          } else {
            onAddRelation(values);
          }
          setSelectedRelation(null);
        }}
        onClose={onCloseModal}
        defaultValue={
          selectedRelation?.defaultValue
            ? (omit(
                selectedRelation.defaultValue,
                relationKeys,
              ) as RelationFieldValue)
            : undefined
        }
        relations={relations}
        isRecommendMode={Boolean(selectedRelation?.defaultValue)}
      />
    </div>
  );
}
