import React, { useEffect } from 'react';
import { ColorPicker } from '@graphpolaris/shared/lib/components/colorComponents/colorPicker';
import { DropdownColorLegend, EntityPill, Input, RelationPill } from '@graphpolaris/shared/lib/components';
import { MapProps } from '../../mapvis';
import { LayerSettingsComponentType } from '../../mapvis.types';
import { nodeColorRGB } from '../../utils';
import { Accordion, AccordionBody, AccordionHead, AccordionItem } from '@graphpolaris/shared/lib/components/accordion';
import { isEqual } from 'lodash-es';

const defaultNodeSettings = (index: number) => ({
  color: nodeColorRGB(index),
  colorMapping: {},
  colorScale: undefined,
  colorByAttribute: false,
  colorAttribute: undefined,
  colorAttributeType: undefined,
  hidden: false,
  shape: 'circle',
  size: 40,
});

const defaultEdgeSettings = () => ({
  hidden: false,
  width: 1,
  sizeAttribute: '',
  fixed: true,
  color: [132, 150, 155],
});

export function NodeLinkOptions({
  settings,
  graphMetadata,
  updateLayerSettings,
  spatialAttributes,
  updateSpatialAttribute,
}: LayerSettingsComponentType<MapProps>) {
  const layerType = 'nodelink';
  const layerSettings = settings[layerType] || { enableBrushing: false, nodes: {}, edges: {} };

  useEffect(() => {
    const nodes = layerSettings.nodes || {};
    const edges = layerSettings.edges || {};

    const newNodes = graphMetadata.nodes.labels.reduce(
      (acc, node, index) => {
        acc[node] = nodes[node] || defaultNodeSettings(index);
        return acc;
      },
      {} as typeof nodes,
    );

    const newEdges = graphMetadata.edges.labels.reduce(
      (acc, edge) => {
        acc[edge] = edges[edge] || defaultEdgeSettings();
        return acc;
      },
      {} as typeof edges,
    );

    if (!isEqual(newNodes, nodes) || !isEqual(newEdges, edges)) {
      updateLayerSettings({
        ...layerSettings,
        nodes: newNodes,
        edges: newEdges,
      });
    }
  }, [graphMetadata]);

  return (
    layerSettings && (
      <div>
        <Accordion defaultOpenAll={true}>
          {graphMetadata.nodes.labels.map((nodeType) => {
            const nodeSettings = layerSettings?.nodes?.[nodeType] || {};

            return (
              <AccordionItem className="mt-2" key={nodeType}>
                <AccordionHead className="flex items-center">
                  <EntityPill title={nodeType} />
                </AccordionHead>

                <AccordionBody>
                  <div>
                    <Input
                      label="Hidden"
                      type="boolean"
                      value={nodeSettings.hidden}
                      onChange={(val) =>
                        updateLayerSettings({ nodes: { ...layerSettings.nodes, [nodeType]: { ...nodeSettings, hidden: val } } })
                      }
                    />
                    <Accordion>
                      <AccordionItem>
                        <AccordionHead>
                          <span className="font-semibold">Location attributes</span>
                        </AccordionHead>
                        <AccordionBody>
                          <Input
                            inline
                            label="Latitude"
                            type="dropdown"
                            value={settings?.location[nodeType]?.lat}
                            options={[...spatialAttributes[nodeType]]}
                            disabled={spatialAttributes[nodeType].length < 1}
                            onChange={(val) => updateSpatialAttribute(nodeType, 'lat', val as string)}
                          />
                          <Input
                            inline
                            label="Longitude"
                            type="dropdown"
                            value={settings?.location[nodeType]?.lon}
                            options={[...spatialAttributes[nodeType]]}
                            disabled={spatialAttributes[nodeType].length < 1}
                            onChange={(val) => updateSpatialAttribute(nodeType, 'lon', val as string)}
                          />
                        </AccordionBody>
                      </AccordionItem>

                      <AccordionItem>
                        <AccordionHead>
                          <div className="flex justify-between items-center">
                            <span className="font-semibold">Color</span>
                            {!nodeSettings.colorByAttribute && (
                              <ColorPicker
                                value={nodeSettings.color}
                                onChange={(val) => {
                                  updateLayerSettings({ nodes: { ...layerSettings.nodes, [nodeType]: { ...nodeSettings, color: val } } });
                                }}
                              />
                            )}
                          </div>
                        </AccordionHead>
                        <AccordionBody>
                          <Input
                            label="By attribute"
                            type="boolean"
                            value={nodeSettings.colorByAttribute ?? false}
                            onChange={(val) =>
                              updateLayerSettings({
                                nodes: { ...layerSettings.nodes, [nodeType]: { ...nodeSettings, colorByAttribute: val } },
                              })
                            }
                          />
                          {nodeSettings.colorByAttribute && (
                            <div>
                              <Input
                                inline
                                label="Color based on"
                                type="dropdown"
                                value={nodeSettings.colorAttribute}
                                options={Object.keys(graphMetadata.nodes.types[nodeType]?.attributes)}
                                onChange={(val) =>
                                  updateLayerSettings({
                                    nodes: {
                                      ...layerSettings.nodes,
                                      [nodeType]: {
                                        ...nodeSettings,
                                        colorAttribute: String(val),
                                        colorAttributeType: graphMetadata.nodes.types[nodeType].attributes[val].dimension,
                                      },
                                    },
                                  })
                                }
                              />
                              {nodeSettings.colorAttributeType === 'numerical' ? (
                                <div>
                                  <p>Select color scale:</p>
                                  <DropdownColorLegend
                                    value={settings?.colorScale}
                                    onChange={(val) =>
                                      updateLayerSettings({
                                        nodes: { ...layerSettings.nodes, [nodeType]: { ...nodeSettings, colorScale: val } },
                                      })
                                    }
                                  />
                                </div>
                              ) : (
                                nodeSettings.colorAttributeType === 'categorical' &&
                                nodeSettings.colorAttribute && (
                                  <div>
                                    {(
                                      graphMetadata.nodes.types[nodeType] as { attributes: { [key: string]: { values: string[] } } }
                                    )?.attributes?.[nodeSettings.colorAttribute]?.values.map((attr: string) => (
                                      <div key={attr} className="flex items-center justify-between">
                                        <p className="truncate w-18">{attr.length > 0 ? attr : 'Empty val'}</p>
                                        <ColorPicker
                                          value={(nodeSettings?.colorMapping ?? {})[attr] ?? [0, 0, 0]}
                                          onChange={(val) => {
                                            updateLayerSettings({
                                              nodes: {
                                                ...layerSettings.nodes,
                                                [nodeType]: {
                                                  ...nodeSettings,
                                                  colorMapping: { ...nodeSettings.colorMapping, [attr]: val },
                                                },
                                              },
                                            });
                                          }}
                                        />
                                      </div>
                                    ))}
                                  </div>
                                )
                              )}
                            </div>
                          )}
                        </AccordionBody>
                      </AccordionItem>
                      <AccordionItem>
                        <AccordionHead>
                          <span className="font-semibold">Shape & Size</span>
                        </AccordionHead>
                        <AccordionBody>
                          <Input
                            inline
                            label="Shape"
                            type="dropdown"
                            value={nodeSettings.shape}
                            options={['circle', 'square', 'triangle', 'diamond', 'location', 'star']}
                            disabled={true}
                            onChange={(val) =>
                              updateLayerSettings({
                                nodes: { ...layerSettings.nodes, [nodeType]: { ...nodeSettings, shape: String(val) } },
                              })
                            }
                          />
                          <Input
                            label="Size"
                            type="slider"
                            min={0}
                            max={80}
                            step={5}
                            value={nodeSettings.size}
                            onChange={(val) =>
                              updateLayerSettings({
                                nodes: { ...layerSettings.nodes, [nodeType]: { ...nodeSettings, size: Number(val) } },
                              })
                            }
                          />
                        </AccordionBody>
                      </AccordionItem>
                    </Accordion>
                  </div>
                </AccordionBody>
              </AccordionItem>
            );
          })}
          {graphMetadata.edges.labels.map((edgeType) => {
            const edgeSettings = layerSettings?.edges?.[edgeType] || {};

            return (
              <AccordionItem className="mt-2" key={edgeType}>
                <AccordionHead className="flex items-center">
                  <RelationPill title={edgeType} />
                </AccordionHead>

                <AccordionBody>
                  <Input
                    label="Hidden"
                    type="boolean"
                    value={edgeSettings.hidden ?? false}
                    onChange={(val) => {
                      updateLayerSettings({ edges: { ...settings.edges, [edgeType]: { ...edgeSettings, hidden: val } } });
                    }}
                  />

                  <Input
                    label="Enable brushing"
                    type="boolean"
                    value={settings.enableBrushing}
                    onChange={(val) => {
                      updateLayerSettings({ enableBrushing: val as boolean });
                    }}
                  />

                  <Accordion>
                    <AccordionItem>
                      <AccordionHead>
                        <span className="font-semibold">Color</span>
                      </AccordionHead>
                      <AccordionBody>
                        <ColorPicker
                          value={edgeSettings.color}
                          onChange={(val) =>
                            updateLayerSettings({ edges: { ...settings.edges, [edgeType]: { ...edgeSettings, color: val } } })
                          }
                        />
                      </AccordionBody>
                    </AccordionItem>
                    <AccordionItem>
                      <AccordionHead>
                        <span className="font-semibold">Width</span>
                      </AccordionHead>
                      <AccordionBody>
                        <Input
                          label="Fixed"
                          type="boolean"
                          value={edgeSettings.fixed}
                          onChange={(val) =>
                            updateLayerSettings({ edges: { ...settings.edges, [edgeType]: { ...edgeSettings, fixed: val } } })
                          }
                        />
                        {!edgeSettings.fixed ? (
                          <div>
                            <Input
                              label="Based on"
                              type="dropdown"
                              size="xs"
                              options={
                                graphMetadata.edges.types[edgeType]?.attributes
                                  ? Object.keys(graphMetadata.edges.types[edgeType].attributes).filter(
                                      (key) => graphMetadata.edges.types[edgeType].attributes[key].dimension === 'numerical',
                                    )
                                  : []
                              }
                              value={edgeSettings.sizeAttribute}
                              onChange={(val) =>
                                updateLayerSettings({
                                  edges: { ...settings.edges, [edgeType]: { ...edgeSettings, sizeAttribute: String(val) } },
                                })
                              }
                            />
                            <div className="flex">
                              <Input
                                type="number"
                                label="min"
                                size="xs"
                                value={edgeSettings.min}
                                onChange={(val) =>
                                  updateLayerSettings({ edges: { ...settings.edges, [edgeType]: { ...edgeSettings, min: val } } })
                                }
                              />
                              <Input
                                type="number"
                                label="max"
                                size="xs"
                                value={edgeSettings.max}
                                onChange={(val) =>
                                  updateLayerSettings({ edges: { ...settings.edges, [edgeType]: { ...edgeSettings, max: val } } })
                                }
                              />
                            </div>
                          </div>
                        ) : (
                          <div>
                            <Input
                              type="slider"
                              label="Width"
                              min={0}
                              max={10}
                              step={0.2}
                              value={edgeSettings.width}
                              onChange={(val) =>
                                updateLayerSettings({ edges: { ...settings.edges, [edgeType]: { ...edgeSettings, width: Number(val) } } })
                              }
                            />
                          </div>
                        )}
                      </AccordionBody>
                    </AccordionItem>
                  </Accordion>
                </AccordionBody>
              </AccordionItem>
            );
          })}
        </Accordion>
      </div>
    )
  );
}