import React, { useEffect, useLayoutEffect, useState } from 'react';
import PropTypes from 'prop-types';
import {
  Button,
} from '@mui/material';
import { styled } from '@mui/material/styles';
import { v4 as uuid } from 'uuid';
import { exportStageSVG } from 'react-konva-to-svg';
import CompanionWindow from 'mirador/dist/es/src/containers/CompanionWindow';
import { OSDReferences } from 'mirador/dist/es/src/plugins/OSDReferences';
import { VideosReferences } from 'mirador/dist/es/src/plugins/VideosReferences';
import Tab from '@mui/material/Tab';
import HighlightAltIcon from '@mui/icons-material/HighlightAlt';
import LayersIcon from '@mui/icons-material/Layers';
import LocalOfferIcon from '@mui/icons-material/LocalOffer';
import HubIcon from '@mui/icons-material/Hub';
import { TabContext, TabList, TabPanel } from '@mui/lab';
import AnnotationDrawing from './annotationForm/AnnotationDrawing';
import WebAnnotation from './WebAnnotation';
import { secondsToHMS } from './utils';
import AnnotationFormContent from './annotationForm/AnnotationFormContent';
import AnnotationFormTime from './annotationForm/AnnotationFormTime';
import AnnotationFormDrawing from './annotationForm/AnnotationFormDrawing';
import { geomFromAnnoTarget, timeFromAnnoTarget } from './AnnotationCreationUtils';

const TARGET_VIEW = 'target';
const OVERLAY_VIEW = 'layer';
const TAG_VIEW = 'tag';
const MANIFEST_LINK_VIEW = 'link';


/** Component for creating annotations.
 * Display in companion window when a manifest is open and an annoation created or edited */
function AnnotationCreation(props) {
  const [toolState, setToolState] = useState({
    activeTool: 'cursor',
    closedMode: 'closed',
    colorPopoverOpen: false,
    currentColorType: false,
    fillColor: 'rgba(255, 0, 0, 0.5)',
    image: { id: null },
    imageEvent: null,
    lineWeightPopoverOpen: false,
    popoverAnchorEl: null,
    popoverLineWeightAnchorEl: null,
    strokeColor: 'green',
    strokeWidth: 3,
  });

  // Initial state setup
  const [state, setState] = useState(() => {
    let tstart;
    let tend;
    const annoState = {};
    if (props.annotation) {
      // annotation body
      if (Array.isArray(props.annotation.body)) {
        annoState.tags = [];
        props.annotation.body.forEach((body) => {
          if (body.purpose === 'tagging' && body.type === 'TextualBody') {
            annoState.tags.push(body.value);
          } else if (body.type === 'TextualBody') {
            annoState.textBody = body.value;
          } else if (body.type === 'Image') {
            // annoState.textBody = body.value; // why text body here ???
            annoState.image = body;
          } else if (body.type === 'AnnotationTitle') {
            annoState.title = body;
          }
        });
      } else if (props.annotation.body.type === 'TextualBody') {
        annoState.textBody = props.annotation.body.value;
      } else if (props.annotation.body.type === 'Image') {
        // annoState.textBody = props.annotation.body.value; // why text body here ???
        annoState.image = props.annotation.body;
      }
      //
      // drawing position
      if (props.annotation.target.selector) {
        if (Array.isArray(props.annotation.target.selector)) {
          props.annotation.target.selector.forEach((selector) => {
            if (selector.type === 'SvgSelector') {
              annoState.svg = selector.value;
            } else if (selector.type === 'FragmentSelector') {
              // TODO proper fragment selector extraction
              annoState.xywh = geomFromAnnoTarget(selector.value);
              [tstart, tend] = timeFromAnnoTarget(selector.value);
            }
          });
        } else {
          annoState.svg = props.annotation.target.selector.value;
          // TODO does this happen ? when ? where are fragments selectors ?
        }
      } else if (typeof props.annotation.target === 'string') {
        annoState.xywh = geomFromAnnoTarget(props.annotation.target);
        [tstart, tend] = timeFromAnnoTarget(props.annotation.target);
      }
    }

    // If we don't have tstart setted, we are creating a new annotation.
    // If we don't have tend setted, we set it at the end of the video.
    // So Tstart is current time and Tend the end of the video
    if (!tstart) {
      tstart = props.currentTime ? Math.floor(props.currentTime) : 0;
      tend = props.mediaVideo ? props.mediaVideo.props.canvas.__jsonld.duration : 0;
    }

    return {
      ...toolState,
      mediaVideo: props.mediaVideo,
      ...annoState,
      tend,
      textEditorStateBustingKey: 0,
      tstart,
      valueTime: [0, 1],
      valuetextTime: '',
    };
  });

  const [shapes, setShapes] = useState([]);
  const [scale, setScale] = useState(1);

  const [value, setValue] = useState(TARGET_VIEW);
  const { height, width } = props.mediaVideo ? props.mediaVideo : 0;

  // TODO Check the effect to keep and remove the other
  // Add a state to trigger redraw
  const [windowSize, setWindowSize] = useState({
    height: window.innerHeight,
    width: window.innerWidth,
  });

  // Listen to window resize event
  useEffect(() => {
    const handleResize = () => {
      setWindowSize({
        width: window.innerWidth,
        height: window.innerHeight,
      });
    };

    window.addEventListener('resize', handleResize);

    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, []);




  useEffect(() => {

  }, [toolState.fillColor, toolState.strokeColor, toolState.strokeWidth]);

  useLayoutEffect(() => {
  }, [{ height, width }]);

  /** */
  const handleImgChange = (newUrl, imgRef) => {
    setToolState((prevState) => ({
      ...prevState,
      image: { ...prevState.image, id: newUrl },
    }));
  };

  /** set annotation start time to current time */
  const setTstartNow = () => {
    setState((prevState) => ({
      ...prevState,
      tstart: Math.floor(props.currentTime),
    }));
  };

  /** set annotation end time to current time */
  const setTendNow = () => {
    setState((prevState) => ({
      ...prevState,
      tend: Math.floor(props.currentTime),
    }));
  };

  /**
     * @param {number} newValueTime
     */
  const setValueTime = (newValueTime) => {
    setState((prevState) => ({
      ...prevState,
      valueTime: newValueTime,
    }));
  };
  const tabHandler = (event, TabIndex) => {
    setValue(TabIndex);
  };
  /**
   * Change from slider
   * @param {Event} event
   * @param {number} newValueTime
   */
  const handleChangeTime = (event, newValueTime) => {
    const timeStart = newValueTime[0];
    const timeEnd = newValueTime[1];
    updateTstart(timeStart);
    updateTend(timeEnd);
    setValueTime(newValueTime);
  };

  /** Change from Tstart HMS Input */
  const updateTstart = (value) => {
    if (value > state.tend) {
      return;
    }
    setState((prevState) => ({
      ...prevState,
      tstart: value,
      ...props.setCurrentTime(value),

    }));
  };

  /** update annotation end time */
  const updateTend = (value) => {
    setState((prevState) => ({
      ...prevState,
      tend: value,
    }));
  };

  // eslint-disable-next-line require-jsdoc
  const seekToTstart = () => {
    setState((prevState) => ({
      ...prevState,
      ...props.setSeekTo(prevState.tstart),
      ...props.setCurrentTime(prevState.tstart),
    }));
  };

  /** */
  const updateGeometry = ({ svg, xywh }) => {
    setState((prevState) => ({
      ...prevState,
      svg,
      xywh,
    }));
  };

  /** */
  const setShapeProperties = (options) => new Promise(() => {
    if (options.fill) {
      state.fillColor = options.fill;
    }

    if (options.strokeWidth) {
      state.strokeWidth = options.strokeWidth;
    }

    if (options.stroke) {
      state.strokeColor = options.stroke;
    }

    setState({ ...state });
  });

  /** */
  const updateTextBody = (textBody) => {
    setState((prevState) => ({
      ...prevState,
      textBody,
    }));
  };

  /**
     * Get SVG picture containing all the stuff draw in the stage (Konva Stage).
     * This image will be put in overlay of the iiif media
     */
  const getSvg = async () => {
    const stage = window.Konva.stages.find((s) => s.attrs.id === props.windowId);
    const svg = await exportStageSVG(stage); // TODO clean
    return svg;
  };


  /** Set color tool from current shape */
  const setColorToolFromCurrentShape = (colorState) => {
    setToolState((prevState) => ({
      ...prevState,
      ...colorState,
    }));
  }


  /** update shapes with shapes from annotationDrawing */

  const updateShapes = (newShapes) => {

    setShapes(newShapes);
  }

  /** delete shape */

  const deleteShape = (shapeId) => {

    const newShapes = shapes.filter((shape) => shape.id !== shapeId);
    setShapes(newShapes);
  }

  /**
     * Validate form and save annotation
     */
  const submitForm = async (e) => {
    e.preventDefault();
    // TODO Possibly problem of syncing
    // TODO Improve this code
    // If we are in edit mode, we have the transformer on the stage saved in the annotation
    if (state.activeTool === 'edit') {
      setState((prevState) => ({
        ...prevState,
        activeTool: 'cursor',
      }));
      return;
    }

    const {
      annotation,
      canvases,
      receiveAnnotation,
      config,
    } = props;

    const {
      title,
      textBody,
      image,
      tags,
      xywh,
      tstart,
      tend,
      textEditorStateBustingKey,
    } = state;

    // TODO rename variable for better comprenhension
    const svg = await getSvg();

    const t = (tstart && tend) ? `${tstart},${tend}` : null;
    const body = { value: (!textBody.length && t) ? `${secondsToHMS(tstart)} -> ${secondsToHMS(tend)}` : textBody };

    // TODO promises not handled. Use promiseAll ?
    canvases.forEach(async (canvas) => {
      const storageAdapter = config.annotation.adapter(canvas.id);
      const anno = new WebAnnotation({
        body,
        canvasId: canvas.id,
        fragsel: {
          t,
          xywh,
        },
        id: (annotation && annotation.id) || `${uuid()}`,
        image,
        manifestId: canvas.options.resource.id,
        svg,
        tags,
        title,
      }).toJson();

      if (annotation) {
        storageAdapter.update(anno)
          .then((annoPage) => {
            receiveAnnotation(canvas.id, storageAdapter.annotationPageId, annoPage);
          });
      } else {
        storageAdapter.create(anno)
          .then((annoPage) => {
            receiveAnnotation(canvas.id, storageAdapter.annotationPageId, annoPage);
          });
      }
    });

    // TODO check if we need other thing in state
    setState({
      image: { id: null },
      svg: null,
      tend: 0,
      textBody: '',
      textEditorStateBustingKey: textEditorStateBustingKey + 1,
      title: '',
      tstart: 0,
      xywh: null,
    });
  };

  /** */
  const {
    annotation,
    closeCompanionWindow,
    id,
    windowId,
  } = props;

  const {
    textBody,
    tstart,
    tend,
    textEditorStateBustingKey,
    valueTime,
  } = state;

  const {
    activeTool,
    fillColor,
    strokeColor,
    strokeWidth,
    closedMode,
    imageEvent,
  } = toolState;

  // TODO : Vérifier ce code, c'est étrange de comprarer un typeof à une chaine de caractère.
  const mediaIsVideo = props.mediaVideo !== 'undefined';
  if (mediaIsVideo) {
    valueTime[0] = tstart;
    valueTime[1] = tend;
  }

  const videoDuration = props.mediaVideo ? props.mediaVideo.props.canvas.__jsonld.duration : 0;
  // TODO: L'erreur de "Ref" sur l'ouverture d'une image vient d'ici et plus particulièrement
  //  du useEffect qui prend en dépedance [overlay.containerWidth, overlay.canvasWidth]
  const videoref = VideosReferences.get(windowId);
  const osdref = OSDReferences.get(windowId);
  let overlay = null;
  if (videoref) {
    overlay = videoref.canvasOverlay;
  }
  if (osdref) {
    console.debug('osdref', osdref);
  }

  /** Change scale from container / canva */
  const updateScale = () => {
    setScale(overlay.containerWidth / overlay.canvasWidth);
  };

  useEffect(() => {
  }, [overlay.containerWidth, overlay.canvasWidth]);

  return (
    // we need to get the width and height of the image to pass it to the annotation drawing component
    <CompanionWindow
      title={annotation ? 'Edit annotation' : 'New annotation'}
      windowId={windowId}
      id={id}
    >
      <StyledAnnotationDrawing
        scale={scale}
        activeTool={activeTool}
        annotation={annotation}
        fillColor={fillColor}
        strokeColor={strokeColor}
        strokeWidth={strokeWidth}
        closed={closedMode === 'closed'}
        updateGeometry={updateGeometry}
        windowId={windowId}
        player={mediaIsVideo ? props.mediaVideo : OSDReferences.get(windowId)}
          // we need to pass the width and height of the image to the annotation drawing component
        width={overlay ? overlay.containerWidth : 1920}
        height={overlay ? overlay.containerHeight : 1080}
        orignalWidth={overlay ? overlay.canvasWidth : 1920}
        orignalHeight={overlay ? overlay.canvasHeight : 1080}
        setShapeProperties={setShapeProperties}
        updateScale={updateScale}
        imageEvent={imageEvent}
        setColorToolFromCurrentShape={setColorToolFromCurrentShape}
        updateShapes={updateShapes}
        shapes={shapes}
        mediaVideo={props.mediaVideo}
      />
      <StyledForm
        onSubmit={submitForm}
      >
        <TabContext value={value}>
          <TabList value={value} onChange={tabHandler} aria-label="icon tabs">
            <StyledTab
              icon={<HighlightAltIcon />}
              aria-label="TargetSelector"
              value={TARGET_VIEW}
            />
            <StyledTab
              icon={<LayersIcon />}
              aria-label="TargetSelector"
              value={OVERLAY_VIEW}
            />
            <StyledTab
              icon={<LocalOfferIcon />}
              aria-label="TargetSelector"
              value={TAG_VIEW}
            />
            <StyledTab
              icon={<HubIcon />}
              aria-label="TargetSelector"
              value={MANIFEST_LINK_VIEW}
            />
          </TabList>
          <StyledTabPanel
            value={TARGET_VIEW}
          >
            {mediaIsVideo && (
              <AnnotationFormTime
                mediaIsVideo={mediaIsVideo}
                videoDuration={videoDuration}
                value={valueTime}
                handleChangeTime={handleChangeTime}
                windowid={windowId}
                setTstartNow={setTstartNow}
                tstart={tstart}
                updateTstart={updateTstart}
                setTendNow={setTendNow}
                tend={tend}
                updateTend={updateTend}
              />
            )}
          </StyledTabPanel>
          <StyledTabPanel
            value={OVERLAY_VIEW}
          >
            <AnnotationFormDrawing
              toolState={toolState}
              updateToolState={setToolState}
              handleImgChange={handleImgChange}
              shapes={shapes}
            />
          </StyledTabPanel>
          <StyledTabPanel
            value={TAG_VIEW}
          >
            <AnnotationFormContent
              textBody={textBody}
              updateTextBody={updateTextBody}
              textEditorStateBustingKey={textEditorStateBustingKey}
            />
          </StyledTabPanel>
          <StyledTabPanel
            value={MANIFEST_LINK_VIEW}
          />
        </TabContext>
        <StyledButtonDivSaveOrCancel>
          <Button onClick={closeCompanionWindow}>
            Cancel
          </Button>
          <Button variant="contained" color="primary" type="submit">
            Save
          </Button>
        </StyledButtonDivSaveOrCancel>
      </StyledForm>
    </CompanionWindow>
  );
}

const StyledButtonDivSaveOrCancel = styled('div')(({ theme }) => ({
  display: 'flex',
  justifyContent: 'flex-end',
}));

const StyledForm = styled('form')(({ theme }) => ({
  display: 'flex',
  flexDirection: 'column',
  gap: '20px',
  paddingBottom: theme.spacing(1),
  paddingLeft: theme.spacing(2),
  paddingRight: theme.spacing(1),
  paddingTop: theme.spacing(2),
}));

const StyledTab = styled(Tab)(({ theme }) => ({
  minWidth: '0px',
  padding: '12px 8px',
}));

const StyledTabPanel = styled(TabPanel)(({ theme }) => ({
  padding: '0',
}));

const StyledAnnotationDrawing = styled(AnnotationDrawing)(({ theme }) => ({
  position: 'absolute',
  top: 0,
  left: 0,
  width: '100%',
  height: 'auto',
}));

AnnotationCreation.propTypes = {
  // TODO proper web annotation type ?
  annotation: PropTypes.object, // eslint-disable-line react/forbid-prop-types
  canvases: PropTypes.arrayOf(
    PropTypes.shape({
      id: PropTypes.string,
      index: PropTypes.number,
    }),
  ),
  closeCompanionWindow: PropTypes.func,

  config: PropTypes.shape({
    annotation: PropTypes.shape({
      adapter: PropTypes.func,
      defaults: PropTypes.objectOf(
        PropTypes.oneOfType(
          [PropTypes.bool, PropTypes.func, PropTypes.number, PropTypes.string],
        ),
      ),
    }),
  }).isRequired,
  currentTime: PropTypes.oneOfType([PropTypes.number, PropTypes.instanceOf(null)]),
  id: PropTypes.string.isRequired,
  receiveAnnotation: PropTypes.func.isRequired,
  setCurrentTime: PropTypes.func,
  setSeekTo: PropTypes.func,
  windowId: PropTypes.string.isRequired,
  mediaVideo: PropTypes.object.isRequired,
};

AnnotationCreation.defaultProps = {
  annotation: null,
  canvases: [],
  closeCompanionWindow: () => {
  },
  currentTime: null,
  paused: true,
  setCurrentTime: () => {
  },
  setSeekTo: () => {
  },
};

export default AnnotationCreation;