'use client'

import { useEffect, useRef } from 'react'
import WithDraggable from '@/components/common/draggable/withDraggable'
import { useRecoilState, useRecoilValue } from 'recoil'
import { useMessage } from '@/hooks/useMessage'
import { useEvent } from '@/hooks/useEvent'
import { canvasState, verticalHorizontalModeState } from '@/store/canvasAtom'
import {
  OUTER_LINE_TYPE,
  outerLineAngle1State,
  outerLineArrow1State,
  outerLineArrow2State,
  outerLineLength1State,
  outerLineLength2State,
  outerLinePointsState,
  outerLineTypeState,
} from '@/store/outerLineAtom'
import { useLine } from '@/hooks/useLine'
import { distanceBetweenPoints } from '@/util/canvas-util'
import { calculateAngle } from '@/util/qpolygon-utils'
import { usePolygon } from '@/hooks/usePolygon'
import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils'

export default function OuterLineWall(props) {
  const { setShowOutlineModal } = props
  const { getMessage } = useMessage()
  const { addCanvasMouseEventListener, addDocumentEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners, removeMouseEvent } =
    useEvent()
  const { addLine, removeLine } = useLine()
  const { addPolygonByLines } = usePolygon()
  const verticalHorizontalMode = useRecoilValue(verticalHorizontalModeState)

  const length1Ref = useRef(null)
  const length2Ref = useRef(null)
  const angle1Ref = useRef(null)
  const [length1, setLength1] = useRecoilState(outerLineLength1State)
  const [length2, setLength2] = useRecoilState(outerLineLength2State)
  const [arrow1, setArrow1] = useRecoilState(outerLineArrow1State)
  const [arrow2, setArrow2] = useRecoilState(outerLineArrow2State)
  const [points, setPoints] = useRecoilState(outerLinePointsState)
  const [type, setType] = useRecoilState(outerLineTypeState)
  const arrow1Ref = useRef(arrow1)
  const arrow2Ref = useRef(arrow2)

  const isFix = useRef(false)

  const [angle1, setAngle1] = useRecoilState(outerLineAngle1State)

  const canvas = useRecoilValue(canvasState)

  useEffect(() => {
    removeMouseEvent('mouse:down', mouseDown)
    addCanvasMouseEventListener('mouse:down', mouseDown)
    clear()
    return () => {
      removeAllMouseEventListeners()
    }
  }, [verticalHorizontalMode, points])

  useEffect(() => {
    arrow1Ref.current = arrow1
  }, [arrow1])

  useEffect(() => {
    arrow2Ref.current = arrow2
  }, [arrow2])

  useEffect(() => {
    removeAllDocumentEventListeners()
    addDocumentEventListener('keydown', document, keydown[type])
    clear()
  }, [type])

  const clear = () => {
    setLength1(0)
    setLength2(0)

    setArrow1('')
    setArrow2('')

    setAngle1(0)
  }

  const mouseDown = (e) => {
    const pointer = canvas.getPointer(e.e)
    if (points.length === 0) {
      setPoints((prev) => [...prev, pointer])
    } else {
      const lastPoint = points[points.length - 1]
      let newPoint = { x: pointer.x, y: pointer.y }
      const length = distanceBetweenPoints(lastPoint, newPoint)
      if (verticalHorizontalMode) {
        const vector = {
          x: pointer.x - points[points.length - 1].x,
          y: pointer.y - points[points.length - 1].y,
        }
        const slope = Math.abs(vector.y / vector.x) // 기울기 계산

        let scaledVector
        if (slope >= 1) {
          // 기울기가 1 이상이면 x축 방향으로 그림
          scaledVector = {
            x: 0,
            y: vector.y >= 0 ? Number(length) : -Number(length),
          }
        } else {
          // 기울기가 1 미만이면 y축 방향으로 그림
          scaledVector = {
            x: vector.x >= 0 ? Number(length) : -Number(length),
            y: 0,
          }
        }

        const verticalLength = scaledVector.y
        const horizontalLength = scaledVector.x

        newPoint = {
          x: lastPoint.x + horizontalLength,
          y: lastPoint.y + verticalLength,
        }
      }
      setPoints((prev) => [...prev, newPoint])
    }
  }

  useEffect(() => {
    canvas
      ?.getObjects()
      .filter((obj) => obj.name === 'outerLine' || obj.name === 'helpGuideLine')
      .forEach((obj) => {
        removeLine(obj)
      })

    canvas?.remove(canvas?.getObjects().find((obj) => obj.name === 'startPoint'))

    // point가 변경 될때마다 이벤트 리스너를 제거하고 다시 등록
    removeAllDocumentEventListeners()
    addDocumentEventListener('keydown', document, keydown[type])

    if (points.length === 0) {
      return
    }

    if (points.length === 1) {
      const point = new fabric.Circle({
        radius: 5,
        fill: 'transparent',
        stroke: 'red',
        left: points[0].x - 5,
        top: points[0].y - 5,
        selectable: false,
        name: 'startPoint',
      })

      canvas?.add(point)
    } else {
      points.forEach((point, idx) => {
        if (idx === 0) {
          return
        }
        drawLine(points[idx - 1], point, idx)
      })

      const lastPoint = points[points.length - 1]
      const firstPoint = points[0]

      if (points.length < 3) {
        return
      }

      /*if (lastPoint.x === firstPoint.x && lastPoint.y === firstPoint.y) {
        return
      }

      if (lastPoint.x === firstPoint.x || lastPoint.y === firstPoint.y) {
        let isAllRightAngle = true

        const firstPoint = points[0]

        points.forEach((point, idx) => {
          if (idx === 0 || !isAllRightAngle) {
            return
          }

          const angle = calculateAngle(point, firstPoint)
          if (angle % 90 !== 0) {
            isAllRightAngle = false
          }
        })

        if (isAllRightAngle) {
          return
        }
        const line = new QLine([lastPoint.x, lastPoint.y, firstPoint.x, firstPoint.y], {
          stroke: 'grey',
          strokeWidth: 1,
          selectable: false,
          name: 'helpGuideLine',
        })

        canvas?.add(line)
        addLineText(line)
      } else {
        const guideLine1 = new QLine([lastPoint.x, lastPoint.y, lastPoint.x, firstPoint.y], {
          stroke: 'grey',
          strokeWidth: 1,
          strokeDashArray: [1, 1, 1],
          name: 'helpGuideLine',
        })

        const guideLine2 = new QLine([guideLine1.x2, guideLine1.y2, firstPoint.x, firstPoint.y], {
          stroke: 'grey',
          strokeWidth: 1,
          strokeDashArray: [1, 1, 1],
          name: 'helpGuideLine',
        })
        if (guideLine1.length > 0) {
          canvas?.add(guideLine1)
          addLineText(guideLine1)
        }

        canvas?.add(guideLine2)

        addLineText(guideLine2)
      }*/
    }
  }, [points])

  const drawLine = (point1, point2, idx) => {
    addLine([point1.x, point1.y, point2.x, point2.y], {
      stroke: 'black',
      strokeWidth: 3,
      idx: idx,
      selectable: false,
      name: 'outerLine',
    })
  }

  const settingLine = () => {
    const outerLines = canvas?.getObjects().filter((obj) => obj.name === 'outerLine')
    outerLines.forEach((line) => {
      removeLine(line)
    })

    addPolygonByLines(outerLines, {
      fill: 'rgba(0,0,0,0)',
      stroke: 'black',
      strokeWidth: 3,
    })
    setShowOutlineModal(false)
  }

  // 직각 완료될 경우 확인
  const checkRightAngle = () => {
    const length1Num = Number(length1Ref.current.value) / 10
    const length2Num = Number(length2Ref.current.value) / 10

    if (points.length === 0) {
      return
    }

    if (length1Num === 0 || length2Num === 0 || arrow1Ref.current === '' || arrow2Ref.current === '') {
      return
    }

    if (arrow1Ref.current === '↓' && arrow2Ref.current === '→') {
      setPoints((prev) => {
        if (prev.length === 0) {
          return []
        }
        return [...prev, { x: prev[prev.length - 1].x + length2Num, y: prev[prev.length - 1].y + length1Num }]
      })
    } else if (arrow1Ref.current === '↓' && arrow2Ref.current === '←') {
      setPoints((prev) => {
        if (prev.length === 0) {
          return []
        }
        return [...prev, { x: prev[prev.length - 1].x - length2Num, y: prev[prev.length - 1].y + length1Num }]
      })
    } else if (arrow1Ref.current === '↑' && arrow2Ref.current === '→') {
      setPoints((prev) => {
        if (prev.length === 0) {
          return []
        }
        return [...prev, { x: prev[prev.length - 1].x + length2Num, y: prev[prev.length - 1].y - length1Num }]
      })
    } else if (arrow1Ref.current === '↑' && arrow2Ref.current === '←') {
      setPoints((prev) => {
        if (prev.length === 0) {
          return []
        }
        return [...prev, { x: prev[prev.length - 1].x - length2Num, y: prev[prev.length - 1].y - length1Num }]
      })
    } else if (arrow1Ref.current === '→' && arrow2Ref.current === '↓') {
      setPoints((prev) => {
        if (prev.length === 0) {
          return []
        }
        return [...prev, { x: prev[prev.length - 1].x + length1Num, y: prev[prev.length - 1].y + length2Num }]
      })
    } else if (arrow1Ref.current === '→' && arrow2Ref.current === '↑') {
      setPoints((prev) => {
        if (prev.length === 0) {
          return []
        }
        return [...prev, { x: prev[prev.length - 1].x + length1Num, y: prev[prev.length - 1].y - length2Num }]
      })
    } else if (arrow1Ref.current === '←' && arrow2Ref.current === '↓') {
      setPoints((prev) => {
        if (prev.length === 0) {
          return []
        }
        return [...prev, { x: prev[prev.length - 1].x - length1Num, y: prev[prev.length - 1].y + length2Num }]
      })
    } else if (arrow1Ref.current === '←' && arrow2Ref.current === '↑') {
      setPoints((prev) => {
        if (prev.length === 0) {
          return []
        }
        return [...prev, { x: prev[prev.length - 1].x - length1Num, y: prev[prev.length - 1].y - length2Num }]
      })
    }
  }

  const keydown = {
    outerLine: (e) => {
      if (points.length === 0) {
        return
      }
      const key = e.key

      if (!length1Ref.current) {
        return
      }

      const lengthNum = Number(length1Ref.current.value) / 10
      if (lengthNum === 0) {
        return
      }
      switch (key) {
        case 'Down': // IE/Edge에서 사용되는 값
        case 'ArrowDown': {
          setArrow1('↓')
          setPoints((prev) => {
            if (prev.length === 0) {
              return []
            }
            return [...prev, { x: prev[prev.length - 1].x, y: prev[prev.length - 1].y + lengthNum }]
          })
          break
        }
        case 'Up': // IE/Edge에서 사용되는 값
        case 'ArrowUp':
          setArrow1('↑')
          setPoints((prev) => {
            if (prev.length === 0) {
              return []
            }
            return [...prev, { x: prev[prev.length - 1].x, y: prev[prev.length - 1].y - lengthNum }]
          })
          break
        case 'Left': // IE/Edge에서 사용되는 값
        case 'ArrowLeft':
          setArrow1('←')
          setPoints((prev) => {
            if (prev.length === 0) {
              return []
            }
            return [...prev, { x: prev[prev.length - 1].x - lengthNum, y: prev[prev.length - 1].y }]
          })
          break
        case 'Right': // IE/Edge에서 사용되는 값
        case 'ArrowRight':
          setArrow1('→')
          setPoints((prev) => {
            if (prev.length === 0) {
              return []
            }
            return [...prev, { x: prev[prev.length - 1].x + lengthNum, y: prev[prev.length - 1].y }]
          })
          break
      }
    },
    rightAngle: (e) => {
      if (points.length === 0) {
        return
      }
      const key = e.key

      const activeElem = document.activeElement

      switch (key) {
        case 'Down': // IE/Edge에서 사용되는 값
        case 'ArrowDown': {
          if (activeElem === length1Ref.current) {
            setArrow1('↓')
            arrow1Ref.current = '↓'
            length2Ref.current.focus()
          } else if (activeElem === length2Ref.current) {
            if (arrow1Ref.current === '↓' || arrow1Ref.current === '↑') {
              break
            }
            setArrow2('↓')
            arrow2Ref.current = '↓'
            checkRightAngle()
          }

          break
        }
        case 'Up': // IE/Edge에서 사용되는 값
        case 'ArrowUp':
          if (activeElem === length1Ref.current) {
            setArrow1('↑')
            arrow1Ref.current = '↑'
            length2Ref.current.focus()
          } else if (activeElem === length2Ref.current) {
            if (arrow1Ref.current === '↓' || arrow1Ref.current === '↑') {
              break
            }
            setArrow2('↑')
            arrow2Ref.current = '↑'
            checkRightAngle()
          }

          break
        case 'Left': // IE/Edge에서 사용되는 값
        case 'ArrowLeft':
          if (activeElem === length1Ref.current) {
            setArrow1('←')
            arrow1Ref.current = '←'
            length2Ref.current.focus()
          } else if (activeElem === length2Ref.current) {
            if (arrow1Ref.current === '←' || arrow1Ref.current === '→') {
              break
            }
            setArrow2('←')
            arrow2Ref.current = '←'
            checkRightAngle()
          }

          break
        case 'Right': // IE/Edge에서 사용되는 값
        case 'ArrowRight':
          if (activeElem === length1Ref.current) {
            setArrow1('→')
            arrow1Ref.current = '→'
            length2Ref.current.focus()
          } else if (activeElem === length2Ref.current) {
            if (arrow1Ref.current === '←' || arrow1Ref.current === '→') {
              break
            }
            setArrow2('→')
            arrow2Ref.current = '→'
            checkRightAngle()
          }

          break
      }
    },
    leeGubae: (e) => {
      console.log('leegubae')
    },
    angle: (e) => {
      const key = e.key
      switch (key) {
        case 'Enter': {
          setPoints((prev) => {
            if (prev.length === 0) {
              return []
            }
            const lastPoint = prev[prev.length - 1]
            const length = length1Ref.current.value / 10
            const angle = angle1Ref.current.value
            //lastPoint로부터 angle1만큼의 각도로 length1만큼의 길이를 가지는 선을 그림
            const radian = (angle * Math.PI) / 180

            const x = lastPoint.x + length * Math.cos(radian)
            const y = lastPoint.y - length * Math.sin(radian)
            return [...prev, { x, y }]
          })
        }
      }
    },
    diagonalLine: (e) => {
      console.log('diagonalLine')
    },
  }

  /**
   * 일변전으로 돌아가기
   */
  const handleRollback = () => {
    //points의 마지막 요소를 제거
    setPoints((prev) => prev.slice(0, prev.length - 1))
  }

  const handleFix = () => {
    if (points.length < 3) {
      return
    }

    let isAllRightAngle = true

    const firstPoint = points[0]

    points.forEach((point, idx) => {
      if (idx === 0 || !isAllRightAngle) {
        return
      }

      const angle = calculateAngle(point, firstPoint)
      if (angle % 90 !== 0) {
        isAllRightAngle = false
      }
    })

    if (isAllRightAngle) {
      alert('부정확한 다각형입니다.')
      return
    }

    setPoints((prev) => {
      return [...prev, { x: prev[0].x, y: prev[0].y }]
    })

    isFix.current = true
  }

  return (
    <WithDraggable isShow={true} pos={{ x: 50, y: -1000 + 50 }}>
      <div className={`modal-pop-wrap ssm`}>
        <div className="modal-head">
          <h1 className="title">{getMessage('modal.cover.outline.drawing')}</h1>
          <button className="modal-close" onClick={() => setShowOutlineModal(false)}>
            닫기
          </button>
        </div>
        <div className="modal-body">
          <div className="modal-btn-wrap">
            <button
              className={`btn-frame modal ${type === OUTER_LINE_TYPE.OUTER_LINE ? 'act' : ''}`}
              onClick={() => setType(OUTER_LINE_TYPE.OUTER_LINE)}
            >
              {getMessage('modal.cover.outline')}
            </button>

            <button
              className={`btn-frame modal ${type === OUTER_LINE_TYPE.RIGHT_ANGLE ? 'act' : ''}`}
              onClick={() => setType(OUTER_LINE_TYPE.RIGHT_ANGLE)}
            >
              {getMessage('modal.cover.outline.right.angle')}
            </button>
            <button
              className={`btn-frame modal ${type === OUTER_LINE_TYPE.LEE_GUBAE ? 'act' : ''}`}
              onClick={() => setType(OUTER_LINE_TYPE.LEE_GUBAE)}
            >
              {getMessage('modal.cover.outline2')}
            </button>
            <button className={`btn-frame modal ${type === OUTER_LINE_TYPE.ANGLE ? 'act' : ''}`} onClick={() => setType(OUTER_LINE_TYPE.ANGLE)}>
              {getMessage('modal.cover.outline.angle')}
            </button>
            <button
              className={`btn-frame modal ${type === OUTER_LINE_TYPE.DIAGONAL_LINE ? 'act' : ''}`}
              onClick={() => setType(OUTER_LINE_TYPE.DIAGONAL_LINE)}
            >
              {getMessage('modal.cover.outline.diagonal')}
            </button>
          </div>
          <div className="modal-check-btn-wrap">
            <h3 className="check-wrap-title">{getMessage('modal.cover.outline.setting')}</h3>
            {type === OUTER_LINE_TYPE.OUTER_LINE ? (
              <div className="outer-line-wrap">
                <div className="form-input">
                  <label htmlFor="">{getMessage('modal.cover.outline.length')}</label>
                  <input
                    type="text"
                    className="input-origin block"
                    value={length1}
                    ref={length1Ref}
                    onChange={(e) => onlyNumberInputChange(e, setLength1)}
                    placeholder="3000"
                  />
                </div>
                <div className="form-input">
                  <label htmlFor="">{getMessage('modal.cover.outline.arrow')}</label>
                  <input type="text" readOnly={true} value={arrow1} className="input-origin block" />
                </div>
              </div>
            ) : type === OUTER_LINE_TYPE.RIGHT_ANGLE ? (
              <div className="outer-line-wrap">
                <div className="form-input">
                  <label htmlFor="">{getMessage('modal.cover.outline.length')}</label>
                  <input
                    type="text"
                    className="input-origin block"
                    value={length1}
                    ref={length1Ref}
                    onChange={(e) => onlyNumberInputChange(e, setLength1)}
                    placeholder="3000"
                  />
                </div>
                <div className="form-input">
                  <label htmlFor="">{getMessage('modal.cover.outline.arrow')}</label>
                  <input type="text" readOnly={true} value={arrow1} className="input-origin block" />
                </div>
                <div className="form-input">
                  <label htmlFor="">{getMessage('modal.cover.outline.length')}</label>
                  <input
                    type="text"
                    className="input-origin block"
                    value={length2}
                    ref={length2Ref}
                    onChange={(e) => onlyNumberInputChange(e, setLength2)}
                    placeholder="3000"
                  />
                </div>
                <div className="form-input">
                  <label htmlFor="">{getMessage('modal.cover.outline.arrow')}</label>
                  <input type="text" readOnly={true} value={arrow2} className="input-origin block" />
                </div>
              </div>
            ) : type === OUTER_LINE_TYPE.ANGLE ? (
              <div className="outer-line-wrap">
                <div className="form-input">
                  <label htmlFor="">{getMessage('modal.cover.outline.length')}</label>
                  <input
                    type="text"
                    className="input-origin block"
                    value={length1}
                    ref={length1Ref}
                    onChange={(e) => onlyNumberInputChange(e, setLength1)}
                    placeholder="3000"
                  />
                </div>
                <div className="form-input">
                  <label htmlFor="">{getMessage('modal.cover.outline.angle')}</label>
                  <input
                    type="text"
                    value={angle1}
                    ref={angle1Ref}
                    onChange={(e) => onlyNumberWithDotInputChange(e, setAngle1)}
                    className="input-origin block"
                  />
                </div>
              </div>
            ) : (
              <></>
            )}
            <div className="flex-check-box for2 btn">
              <button className={`arr-btn dark ${points.length >= 3 ? 'act' : ''}`} onClick={handleFix}>
                <span>{getMessage('modal.cover.outline.fix')}</span>
              </button>
              <button className={`arr-btn dark ${points.length >= 1 ? 'act' : ''}`} onClick={handleRollback}>
                <span>{getMessage('modal.cover.outline.rollback')}</span>
              </button>
              <button className="arr-btn dark">
                <span>{getMessage('modal.cover.outline.remove')}</span>
              </button>
              <button className="arr-btn dark">
                <span>{getMessage('modal.cover.outline.select.move')}</span>
              </button>
            </div>
          </div>
        </div>
      </div>
    </WithDraggable>
  )
}
