import * as uuid from "uuid";

import { type Datum } from "./datum.svelte.ts";
import { 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 === "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
        ? `${value.c.getFullYear()}-${
          value.c.getMonth() + 1
        }-${value.c.getDate()}`
        : "",
      is_null: value.c === undefined,
      time_value: value.c
        ? `${value.c.getHours()}:${value.c.getMinutes()}`
        : "",
    };
  } 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 === "Text") {
    return { t: "Text", c: value.text_value };
  }
  if (presentation.t === "Timestamp") {
    // FIXME
    throw new Error("not yet implemented");
  }
  if (presentation.t === "Uuid") {
    try {
      return { t: "Uuid", c: uuid.stringify(uuid.parse(value.text_value)) };
    } catch {
      // uuid.parse() throws a TypeError if unsuccessful.
      return undefined;
    }
  }
  type _ = Assert<typeof presentation extends never ? true : false>;
  throw new Error("this should be unreachable");
}
