import { Layout, Txt, makeScene2D } from "@motion-canvas/2d";
import {
  Direction,
  all,
  beginSlide,
  createRef,
  makeRef,
  slideTransition,
  waitFor,
} from "@motion-canvas/core";

import { FunctionBox } from "../components/function_box";
import { theme } from "../theme";
import { PEOPLE, Person, PersonI } from "../components/person";

export const daysUntilNextDate = (date: Date): number => {
  const today = new Date();

  const nextBirthday = new Date(
    today.getFullYear(),
    date.getMonth(),
    date.getDate(),
  );
  if (today > nextBirthday) nextBirthday.setFullYear(today.getFullYear() + 1);

  const timeDiff = nextBirthday.getTime() - today.getTime();
  return Math.ceil(timeDiff / (1000 * 3600 * 24));
};

export interface CardI {
  message: string;
  deliverInDays: number;
}

export const birthdayCardsFor = (people: PersonI[]): CardI[] => {
  const cards: CardI[] = [];

  for (const person of people) {
    const age = new Date().getFullYear() - person.birthday.getFullYear();
    const ending =
      ({ 1: "st", 2: "nd", 3: "rd" } as Record<number, string>)[
        parseInt(Array.from(age.toString()).at(-1))
      ] ?? "th";
    const message =
      `Happy ${age}${ending} birthday, ${person.name}!\n` +
      "I can't believe it's already been a year!";

    const deliverInDays = daysUntilNextDate(person.birthday);
    cards.push({ deliverInDays, message });
  }

  return cards;
};

const birthdayCardsSrc = `export interface CardI {
  message: string;
  deliverInDays: number;
}

const birthdayCardsFor = (people: PersonI[]): CardI[] => {
  const cards: CardI[] = [];

  for (const person of people) {
    const age = new Date().getFullYear() - person.birthday.getFullYear();
    const ageEnding = (
      { 1: "st", 2: "nd", 3: "rd" } as Record<number, string>
    )[parseInt(Array.from(age.toString()).at(-1))] ?? "th";

    const message =
      \`Happy \${age}\${ageEnding} birthday, \${person.name}!\\n\` +
      "I can't believe it's already been a year!";

    const deliverInDays = daysUntilNextDate(person.birthday);
    cards.push({ deliverInDays, message });
  }

  return cards;
};`;

export default makeScene2D(function* (view) {
  const layout = createRef<Layout>();
  const date = createRef<Txt>();
  const functionBox = createRef<FunctionBox>();
  const people: Person[] = [];
  const peopleLayout: Layout[] = [];
  const peopleText: Txt[] = [];

  view.add(
    <FunctionBox
      ref={functionBox}
      source={birthdayCardsSrc}
      fn={birthdayCardsFor}
      workingText="📝⚙"
      outputFontSize={25}
    ></FunctionBox>,
  );

  yield* all(slideTransition(Direction.Left), functionBox().showCode(0.75));
  yield* functionBox().reset(0.1);

  yield* beginSlide("Show code");

  yield* functionBox().reset(0.1);
  yield* all(
    functionBox().hideCode(0.8),
    functionBox().setInputs(
      [
        {
          val: PEOPLE,
          node: (
            <Layout direction="column" gap={5} layout>
              {PEOPLE.map((person) => (
                <Person person={person} />
              ))}
            </Layout>
          ),
        },
      ],
      0.8,
    ),
  );

  yield* beginSlide("Show people");

  yield* functionBox().propogateInput(0.6);
  yield* functionBox().propogateOutput(0.6);
  yield* beginSlide("Generate birthday cards");

  yield* functionBox().opacity(0, 0.5);
  functionBox().remove();

  view.add(
    <Layout opacity={0} ref={layout} direction="column" gap={15} layout>
      <Txt
        fontSize={30}
        fontFamily={theme.font}
        fill={theme.text.hex}
        ref={date}
      />

      <Layout direction="row" gap={15}>
        {PEOPLE.map((person, i) => (
          <Layout
            ref={makeRef(peopleLayout, i)}
            alignItems="center"
            direction="column"
            gap={100}
          >
            <Person ref={makeRef(people, i)} person={person} />
            <Txt
              textAlign="center"
              ref={makeRef(peopleText, i)}
              fontSize={0}
              fontFamily={theme.font}
              fill={theme.text.hex}
            />
          </Layout>
        ))}
      </Layout>
    </Layout>,
  );
  yield* layout().opacity(1, 0.5);

  const cards = birthdayCardsFor(PEOPLE);

  for (let i = 1; i <= 365 + 1; i++) {
    const day = new Date(Date.now() + i * 24 * 60 * 60 * 1000);

    yield* waitFor(0.02);
    yield date().text(day.toDateString());

    const peoplesBirthday = cards
      .map((x, i) => [x, i] as [CardI, number])
      .filter(([{ deliverInDays }]) => deliverInDays === i)
      .map(([_, i]) => i);

    yield* all(
      ...peoplesBirthday.map((p) => {
        const text = peopleText[p];
        return all(
          text.text("🗓️🎂\n" + cards[p].message, 0),
          text.fontSize(20, 0.5),
        );
      }),
    );

    yield* all(
      ...peoplesBirthday.map((p) => {
        const layout = peopleLayout[p];
        return layout.gap(0, 0.5);
      }),
    );

    yield* all(
      ...peoplesBirthday.map((p) => {
        const person = people[p];
        const text = peopleText[p];
        return all(
          text.opacity(0, 0.5),
          text.fontSize(0, 0.5),
          person.emit("🥳", 0.8, false),
        );
      }),
    );
  }

  yield* beginSlide("See their reactions");
  yield* all(
    date().fontSize(0, 0.5),
    date().opacity(0, 0.65),
    ...people.map((person) => person.opacity(0, 0.5)),
  );
});
