import { format as format_date, parse as parse_date } from "date-fns";
import { utc } from "@date-fns/utc";

import { type Datum, parse_datum_from_text } from "./datum.svelte.ts";
import {
  get_empty_datum_for,
  type Presentation,
} from "./presentation.svelte.ts";

type Assert<_T extends true> = void;

// This should be a discriminated union type, but TypeScript isn't
// sophisticated enough to discriminate based on the nested presentation's tag,
// causing a huge pain in the ass.
export type EditorState = {
  date_value: string;
  text_value: string;
  time_value: string;
  is_null: boolean;
};

export const DEFAULT_EDITOR_STATE: EditorState = {
  date_value: "",
  text_value: "",
  time_value: "",
  is_null: false,
};

export function editor_state_from_datum(value: Datum): EditorState {
  if (value.t === "Numeric" || value.t === "Text") {
    return {
      ...DEFAULT_EDITOR_STATE,
      text_value: value.c ?? "",
      is_null: value.c === undefined,
    };
  } else if (value.t === "Timestamp") {
    return {
      ...DEFAULT_EDITOR_STATE,
      date_value: value.c
        // FIXME: time zones
        ? format_date(value.c, "yyyy-MM-dd")
        : "",
      is_null: value.c === undefined,
      time_value: value.c ? format_date(value.c, "HH:mm:ss") : "",
    };
  } else if (value.t === "Uuid") {
    return {
      ...DEFAULT_EDITOR_STATE,
      text_value: value.c ?? "",
      is_null: value.c === undefined,
    };
  }
  type _ = Assert<typeof value extends never ? true : false>;
  throw new Error("this should be unreachable");
}

export function datum_from_editor_state(
  value: EditorState,
  presentation: Presentation,
): Datum | undefined {
  if (
    presentation.t === "Dropdown" || presentation.t === "Numeric" ||
    presentation.t === "Text" || presentation.t === "Uuid"
  ) {
    // Presentations which derive values directly from the editor's text input
    // can all be handled with the same logic.
    if (value.is_null) {
      return get_empty_datum_for(presentation);
    }
    const parsed = parse_datum_from_text(presentation, value.text_value);
    if (parsed.c === undefined) {
      return undefined;
    }
    return parsed;
  }
  if (presentation.t === "Timestamp") {
    if (value.is_null) {
      return get_empty_datum_for(presentation);
    }
    if (value.date_value === "" || value.time_value === "") {
      return undefined;
    }
    const parsed = parse_date(
      `${value.date_value}T${value.time_value}`,
      "yyyy-MM-dd'T'HH:mm:ss",
      new Date(),
      { in: utc },
    );
    if (Number.isNaN(parsed)) {
      return undefined;
    }
    return { t: "Timestamp", c: parsed };
  }
  type _ = Assert<typeof presentation extends never ? true : false>;
  throw new Error("this should be unreachable");
}
