"use client"

import * as React from "react"

import { useT } from "@/components/locale-provider"
import { IconCloudUpload } from "@tabler/icons-react"

import { Button } from "@/components/ui/button"
import { cn } from "@/lib/utils"
import { loadHolidays } from "./actions"

/**
 * Getting the year's calendar into the system.
 *
 * A file, dropped or chosen, and nothing else. There was a textarea to paste
 * into as well, and it was a second way to do one thing: the calendar always
 * exists as a file, and a box of pasted text is a place for half of one to end
 * up. The example is downloadable so somebody with a spreadsheet has something
 * to fill in rather than a format to guess at.
 *
 * The file loads on drop. Asking for a second press after the drop would be a
 * confirmation of something already decided by the act of dropping it, and what
 * needs confirming here is not the file but the result, which the answer gives.
 *
 * A refusal names the line. The file is a year of somebody's work, and "invalid
 * CSV" leaves them to find the one bad row themselves.
 */

/**
 * The example, written here rather than kept as a file on disk.
 *
 * It has to say what the parser actually accepts, and a file in `public/` is a
 * second copy of that truth which drifts the first time the rules change. The
 * third column is in it on purpose: extra columns are ignored, and somebody
 * whose spreadsheet has six of them should see that before they spend an
 * afternoon deleting four.
 */
const SAMPLE = `date,name,note
2026-01-01,New Year's Day,A public holiday
2026-03-03,Makha Bucha,
2026-04-13,Songkran,"Three days, 13 to 15 April"
2026-04-14,Songkran,
2026-04-15,Songkran,
2026-12-05,วันพ่อแห่งชาติ,The Thai name is kept exactly as written
`

export function ImportForm() {
  const t = useT()
  const input = React.useRef<HTMLInputElement>(null)
  const [pending, setPending] = React.useState(false)
  const [problem, setProblem] = React.useState<string | null>(null)
  const [done, setDone] = React.useState<string | null>(null)
  const [over, setOver] = React.useState(false)

  async function take(file: File | undefined) {
    if (!file) return
    setProblem(null)
    setDone(null)

    /**
     * Checked by extension and not by MIME type. A `.csv` out of a spreadsheet
     * arrives as text/csv, application/vnd.ms-excel or nothing at all depending
     * on the machine, and refusing on that basis would refuse files that are
     * perfectly readable.
     */
    if (!/\.(csv|txt)$/i.test(file.name)) {
      setProblem(t("holidays.notACsv"))
      return
    }

    setPending(true)
    try {
      const result = await loadHolidays(await file.text())
      if (result.ok) {
        setDone(
          t("holidays.loaded", {
            added: result.report.added,
            years: result.report.years.join(", "),
            removed: result.report.removed,
          }),
        )
        return
      }
      setProblem(
        t("holidays.lineProblem", {
          line: result.line,
          problem: result.problem,
        }),
      )
    } catch {
      setProblem(t("refusal.noAnswer"))
    } finally {
      setPending(false)
      // So the same file can be chosen twice, which is what somebody does after
      // correcting the line the refusal named.
      if (input.current) input.current.value = ""
    }
  }

  /**
   * The example is built in the browser rather than fetched. There is no file
   * to keep in sync and nothing to reach the network for, which matters on a
   * machine with no way out to one.
   */
  function downloadSample() {
    const url = URL.createObjectURL(
      new Blob([SAMPLE], { type: "text/csv;charset=utf-8" }),
    )
    const a = document.createElement("a")
    a.href = url
    a.download = "holiday-calendar-example.csv"
    a.click()
    URL.revokeObjectURL(url)
  }

  return (
    /*
      Full width, per the rule this project set the first time a card was left
      floating with several hundred pixels of nothing beside it: the content
      fills what it is given, and only prose sets its own measure. The drop zone
      is a target, not a paragraph, and a target you have to aim at is a worse
      target.
    */
    <div className="flex w-full flex-col gap-3">
      {/*
        The whole area is the control, not a button inside it. Somebody who
        cannot drag has the same target as somebody who can, and it is the
        largest thing on the card rather than a small button beside a big
        rectangle that does nothing.
      */}
      <div
        role="button"
        tabIndex={0}
        onClick={() => input.current?.click()}
        onKeyDown={(e) => {
          if (e.key === "Enter" || e.key === " ") {
            e.preventDefault()
            input.current?.click()
          }
        }}
        onDragOver={(e) => {
          e.preventDefault()
          setOver(true)
        }}
        onDragLeave={() => setOver(false)}
        onDrop={(e) => {
          e.preventDefault()
          setOver(false)
          void take(e.dataTransfer.files[0])
        }}
        className={cn(
          "flex cursor-pointer flex-col items-center justify-center gap-2 rounded-xl border-2 border-dashed px-6 py-12 text-center transition-colors",
          "focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:outline-none",
          over
            ? "border-primary bg-primary/5"
            : "border-input hover:border-foreground/30 hover:bg-muted/40",
          pending && "pointer-events-none opacity-60",
        )}
      >
        <IconCloudUpload
          className={cn(
            "size-9 transition-colors",
            over ? "text-primary" : "text-foreground/50",
          )}
          aria-hidden
        />
        <p className="text-lg font-medium">
          {pending
            ? t("holidays.loading")
            : over
              ? t("holidays.dropping")
              : t("holidays.drop")}
        </p>
        <p className="text-sm text-foreground/70">{t("holidays.orBrowse")}</p>

        <input
          ref={input}
          type="file"
          accept=".csv,.txt,text/csv,text/plain"
          className="sr-only"
          onChange={(e) => void take(e.target.files?.[0])}
        />
      </div>

      {/*
        Centred under the zone, because the zone is the whole width now and
        these two belong to it. Pinned left they read as the start of a new
        column beside a target that has no left edge to speak of.
      */}
      <div className="flex justify-center">
        <Button variant="outline" size="sm" onClick={downloadSample}>
          {t("holidays.sample")}
        </Button>
      </div>

      {/* A sentence, so it keeps a measure somebody can read across. */}
      <p className="mx-auto max-w-prose text-center text-sm text-foreground/70">
        {t("holidays.importHint")}
      </p>

      {problem ? (
        <p className="text-center text-sm text-destructive">{problem}</p>
      ) : null}
      {done ? (
        <p className="text-center text-sm text-foreground/80">{done}</p>
      ) : null}
    </div>
  )
}
