import { AbsoluteTime } from "@gnu-taler/taler-util";
import {
  add as dateAdd,
  sub as dateSub,
  eachDayOfInterval,
  endOfMonth,
  endOfWeek,
  format,
  getMonth,
  getYear,
  isSameDay,
  isSameMonth,
  isValid,
  setYear,
  startOfDay,
  startOfMonth,
  startOfWeek,
  subYears,
} from "date-fns";
import { VNode, h } from "preact";
import { useEffect, useRef, useState } from "preact/hooks";
import { useTranslationContext } from "../index.browser.js";
import { composeRef, saveRef } from "../components/utils.js";

const THIS_MONTH = getMonth(new Date());
const THIS_YEAR = getYear(new Date());
const TODAY = startOfDay(new Date());

export function Calendar({
  value,
  onChange,
}: {
  value: AbsoluteTime | undefined;
  onChange: (v: AbsoluteTime) => void;
}): VNode {
  const selectedMaybeInvalid = !value
    ? TODAY
    : new Date(AbsoluteTime.toStampMs(value));
  const selected = isValid(selectedMaybeInvalid) ? selectedMaybeInvalid : TODAY;
  const [showingDate, setShowingDate] = useState(selected);
  const m = getMonth(showingDate);
  const y = getYear(showingDate);
  const month = Number.isNaN(m) ? THIS_MONTH : m;
  const year = Number.isNaN(y) ? THIS_YEAR : y;
  const input = useRef<HTMLInputElement>();
  useEffect(() => {
    if (!input.current) return;
    if (input.current === document.activeElement) return;
    input.current.value = !year ? "" : String(year);
  }, [year]);

  const start = startOfWeek(startOfMonth(showingDate));
  const end = endOfWeek(endOfMonth(showingDate));
  const daysInMonth = eachDayOfInterval({ start, end });
  const { i18n } = useTranslationContext();
  const monthNames = [
    i18n.str`January`,
    i18n.str`February`,
    i18n.str`March`,
    i18n.str`April`,
    i18n.str`May`,
    i18n.str`June`,
    i18n.str`July`,
    i18n.str`August`,
    i18n.str`September`,
    i18n.str`October`,
    i18n.str`November`,
    i18n.str`December`,
  ];
  return (
    <div class="text-center p-2">
      <div class="flex items-center text-gray-900">
        <button
          type="button"
          class="flex px-4 flex-none items-center justify-center p-1.5 text-gray-400 hover:text-gray-500 ring-2 round-sm"
          onClick={() => {
            setShowingDate(dateSub(showingDate, { years: 1 }));
          }}
        >
          <span class="sr-only">{i18n.str`Previous year`}</span>
          <svg
            class="h-5 w-5"
            viewBox="0 0 20 20"
            fill="currentColor"
            aria-hidden="true"
          >
            <path
              fill-rule="evenodd"
              d="M12.79 5.23a.75.75 0 01-.02 1.06L8.832 10l3.938 3.71a.75.75 0 11-1.04 1.08l-4.5-4.25a.75.75 0 010-1.08l4.5-4.25a.75.75 0 011.06.02z"
              clip-rule="evenodd"
            />
          </svg>
        </button>
        <div class="flex-auto text-sm font-semibold">
          <input
            type="text"
            ref={composeRef(saveRef(input))}
            onChange={(e) => {
              const text = e.currentTarget.value;
              const num = Number.parseInt(text, 10);

              if (Number.isSafeInteger(num) && num > 0) {
                const nextYear = setYear(showingDate, num);

                if (isValid(nextYear)) {
                  setShowingDate(nextYear);
                }
              }
            }}
          />
        </div>
        <button
          type="button"
          class="flex px-4 flex-none items-center justify-center p-1.5 text-gray-400 hover:text-gray-500 ring-2 round-sm"
          onClick={() => {
            setShowingDate(dateAdd(showingDate, { years: 1 }));
          }}
        >
          <span class="sr-only">{i18n.str`Next year`}</span>
          <svg
            class="h-5 w-5"
            viewBox="0 0 20 20"
            fill="currentColor"
            aria-hidden="true"
          >
            <path
              fill-rule="evenodd"
              d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z"
              clip-rule="evenodd"
            />
          </svg>
        </button>
      </div>
      <div class="mt-4 flex items-center text-gray-900">
        <button
          type="button"
          class="flex px-4 flex-none items-center justify-center p-1.5 text-gray-400 hover:text-gray-500 ring-2 round-sm"
          onClick={() => {
            setShowingDate(dateSub(showingDate, { months: 1 }));
          }}
        >
          <span class="sr-only">{i18n.str`Previous month`}</span>
          <svg
            class="h-5 w-5"
            viewBox="0 0 20 20"
            fill="currentColor"
            aria-hidden="true"
          >
            <path
              fill-rule="evenodd"
              d="M12.79 5.23a.75.75 0 01-.02 1.06L8.832 10l3.938 3.71a.75.75 0 11-1.04 1.08l-4.5-4.25a.75.75 0 010-1.08l4.5-4.25a.75.75 0 011.06.02z"
              clip-rule="evenodd"
            />
          </svg>
        </button>
        <div class="flex-auto text-sm font-semibold">{monthNames[month]}</div>
        <button
          type="button"
          class="flex px-4 flex-none items-center justify-center p-1.5 text-gray-400 hover:text-gray-500 ring-2 rounded-sm "
          onClick={() => {
            setShowingDate(dateAdd(showingDate, { months: 1 }));
          }}
        >
          <span class="sr-only">{i18n.str`Next month`}</span>
          <svg
            class="h-5 w-5"
            viewBox="0 0 20 20"
            fill="currentColor"
            aria-hidden="true"
          >
            <path
              fill-rule="evenodd"
              d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z"
              clip-rule="evenodd"
            />
          </svg>
        </button>
      </div>
      <div class="mt-6 grid grid-cols-7 text-xs leading-6 text-gray-500">
        <div>M</div>
        <div>T</div>
        <div>W</div>
        <div>T</div>
        <div>F</div>
        <div>S</div>
        <div>S</div>
      </div>
      <div class="isolate mt-2">
        <div class="grid grid-cols-7 gap-px rounded-lg bg-gray-200 text-sm shadow ring-1 ring-gray-200">
          {daysInMonth.map((current, idx) => (
            <button
              type="button"
              key={idx}
              data-month={isSameMonth(current, showingDate)}
              data-today={isSameDay(current, TODAY)}
              data-selected={isSameDay(current, selected)}
              onClick={() => {
                onChange(AbsoluteTime.fromStampMs(current.getTime()));
              }}
              class="text-gray-400 hover:bg-gray-700 focus:z-10 py-1.5 
          data-[month=false]:bg-gray-100 data-[month=true]:bg-white 
            data-[today=true]:font-semibold  
          data-[month=true]:text-gray-900
          data-[today=true]:bg-red-300 data-[today=true]:hover:bg-red-200
          data-[month=true]:hover:bg-gray-200
          data-[selected=true]:!bg-blue-400 data-[selected=true]:hover:!bg-blue-300 "
            >
              <time
                dateTime={format(current, "yyyy-MM-dd")}
                class="mx-auto flex h-7 w-7 py-4 px-5 sm:px-8 items-center justify-center rounded-full"
              >
                {format(current, "dd")}
              </time>
            </button>
          ))}
        </div>
        {daysInMonth.length < 40 ? <div class="w-7 h-7 m-1.5" /> : undefined}
      </div>
    </div>
  );
}
