import { Component } from 'react';
import { type SxProps } from '@mui/material';
import isUndefined from 'lodash/isUndefined';

import { getColor, mergeProps } from '../Util';
import { styled } from '../Theme';

const SECONDS_OF_MINUTE = 60;
const SECONDS_OF_HOUR = 60 * SECONDS_OF_MINUTE;
const SECONDS_OF_DAY = 24 * SECONDS_OF_HOUR;

function getRemaining(endTime: number) {
  const now = new Date().getTime();
  const diff = Math.round((endTime - now) / 1000);
  const days = Math.floor(diff / SECONDS_OF_DAY);
  const hours = Math.floor((diff % SECONDS_OF_DAY) / SECONDS_OF_HOUR);
  const minutes = Math.floor(((diff % SECONDS_OF_DAY) % SECONDS_OF_HOUR) / SECONDS_OF_MINUTE);
  const seconds = Math.round(((diff % SECONDS_OF_DAY) % SECONDS_OF_HOUR) % SECONDS_OF_MINUTE);
  return { days, hours, minutes, seconds, done: diff <= 0 };
}

// eslint-disable-next-line react/prop-types
function FixWidthNumber({ number, label, length = 2 }: { number: number; label: string; length?: number }) {
  let str = number.toString();
  while (str.length < length) {
    str = `0${str}`;
  }

  const numbers = str.split('');
  return (
    <span className="num-group">
      <span className="num-items">
        {numbers.map((x, i) => (
          // eslint-disable-next-line react/no-array-index-key
          <strong key={`num-${i}`} className="num-item">
            {x}
          </strong>
        ))}
      </span>
      <span className="num-label">{label}</span>
    </span>
  );
}

export interface CountDownProps extends React.HTMLAttributes<HTMLDivElement> {
  /** endTime should be UTC */
  endTime: number;
  // eslint-disable-next-line react/require-default-props
  dark?: true | false;
  // eslint-disable-next-line react/require-default-props
  sx?: SxProps;
}

export interface CountDownState {
  days: number;
  hours: number;
  minutes: number;
  seconds: number;
  done: boolean;
}

export default class CountDown extends Component<CountDownProps, CountDownState> {
  // eslint-disable-next-line react/static-property-placement
  static defaultProps: { dark: boolean; style: {} };

  timer?: NodeJS.Timeout;

  constructor(rawProps: CountDownProps) {
    const props = Object.assign({}, rawProps);
    if (isUndefined(props.dark)) {
      props.dark = true;
    }
    if (isUndefined(props.style)) {
      props.style = {};
    }
    super(props);
    const newProps = mergeProps(props, CountDown, ['dark', 'endTime', 'style']);
    this.state = getRemaining(newProps.endTime);
    this.timer = undefined;
  }

  componentDidMount() {
    this.timer = setInterval(() => {
      const remaining = getRemaining(this.props.endTime);
      if (remaining.done) {
        clearInterval(this.timer);
        this.timer = undefined;
      }

      this.setState(remaining);
    }, 1000);
  }

  componentWillUnmount() {
    if (this.timer) {
      clearInterval(this.timer);
      this.timer = undefined;
    }
  }

  render() {
    const { dark, endTime, ...rest } = this.props;
    const { days, hours, minutes, seconds } = this.state;
    return (
      <Container dark={dark!} {...rest}>
        <FixWidthNumber number={days} label="D" />
        <span className="num-sep">:</span>
        <FixWidthNumber number={hours} label="H" />
        <span className="num-sep">:</span>
        <FixWidthNumber number={minutes} label="M" />
        <span className="num-sep">:</span>
        <FixWidthNumber number={seconds} label="S" />
      </Container>
    );
  }
}

const textBackground = `linear-gradient(to bottom, rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.4)),
linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.5) 77%, rgba(0, 0, 0, 0.5))`;

const Container = styled('div', { shouldForwardProp: (prop) => prop !== 'dark' })<{ dark: boolean }>`
  color: ${(props) => getColor(props)};
  font-size: 50px;
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: flex-start;

  .num-group {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    margin: 0 60px;

    @media (max-width: ${(props) => props.theme.breakpoints.values.md}px) {
      margin: 0 20px;
    }
    @media (max-width: ${(props) => props.theme.breakpoints.values.sm}px) {
      margin: 0;
    }

    .num-items {
      display: flex;
      flex-direction: row;
    }

    .num-item {
      display: inline-block;
      width: 40px;
      height: 60px;
      line-height: 60px;
      background-image: ${(props) => (props.dark ? textBackground : 'none')};
      text-align: center;

      &:first-of-type {
        margin-right: 10px;
        @media (max-width: ${(props) => props.theme.breakpoints.values.sm}px) {
          margin: 0;
        }
      }
    }

    .num-label {
      margin-top: 20px;
      font-size: 24px;
      color: ${(props) => getColor(props)};
    }
  }
`;
