"use client"

import * as React from "react"
import {
  Copy01Icon,
  Globe02Icon,
  HandPointingRight04Icon,
  Tick02Icon,
} from "@hugeicons/core-free-icons"
import { HugeiconsIcon } from "@hugeicons/react"

import { cn } from "@/lib/utils"
import { useConfig } from "@/hooks/use-config"
import { copyToClipboardWithMeta } from "@/components/copy-button"
import {
  BASES,
  buildThemeForPreset,
  DEFAULT_CONFIG,
  type BaseName,
  type DesignSystemConfig,
} from "@/registry/config"
import { Button } from "@/styles/base-nova/ui/button"
import {
  Dialog,
  DialogContent,
  DialogFooter,
  DialogHeader,
  DialogTrigger,
} from "@/styles/base-nova/ui/dialog"
import {
  Field,
  FieldContent,
  FieldDescription,
  FieldGroup,
  FieldLabel,
  FieldLegend,
  FieldSeparator,
  FieldSet,
  FieldTitle,
} from "@/styles/base-nova/ui/field"
import { RadioGroup, RadioGroupItem } from "@/styles/base-nova/ui/radio-group"
import { Switch } from "@/styles/base-nova/ui/switch"
import {
  Tabs,
  TabsContent,
  TabsList,
  TabsTrigger,
} from "@/styles/base-nova/ui/tabs"
import {
  ToggleGroup,
  ToggleGroupItem,
} from "@/styles/base-nova/ui/toggle-group"
import { usePresetCode } from "@/app/(app)/(create)/hooks/use-design-system"
import {
  useDesignSystemSearchParams,
  type DesignSystemSearchParams,
} from "@/app/(app)/(create)/lib/search-params"
import {
  getFramework,
  getTemplateValue,
  NO_MONOREPO_FRAMEWORKS,
  TEMPLATES,
} from "@/app/(app)/(create)/lib/templates"

const TURBOREPO_LOGO =
  '<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Turborepo</title><path d="M11.9906 4.1957c-4.2998 0-7.7981 3.501-7.7981 7.8043s3.4983 7.8043 7.7981 7.8043c4.2999 0 7.7982-3.501 7.7982-7.8043s-3.4983-7.8043-7.7982-7.8043m0 11.843c-2.229 0-4.0356-1.8079-4.0356-4.0387s1.8065-4.0387 4.0356-4.0387S16.0262 9.7692 16.0262 12s-1.8065 4.0388-4.0356 4.0388m.6534-13.1249V0C18.9726.3386 24 5.5822 24 12s-5.0274 11.66-11.356 12v-2.9139c4.7167-.3372 8.4516-4.2814 8.4516-9.0861s-3.735-8.749-8.4516-9.0861M5.113 17.9586c-1.2502-1.4446-2.0562-3.2845-2.2-5.3046H0c.151 2.8266 1.2808 5.3917 3.051 7.3668l2.0606-2.0622zM11.3372 24v-2.9139c-2.02-.1439-3.8584-.949-5.3019-2.2018l-2.0606 2.0623c1.975 1.773 4.538 2.9022 7.361 3.0534z"/></svg>'
const ORIGIN = process.env.NEXT_PUBLIC_APP_URL || "http://localhost:4000"
const IS_LOCAL_DEV = ORIGIN.includes("localhost")
const SHADCN_VERSION = process.env.NEXT_PUBLIC_RC ? "@rc" : "@latest"
const PACKAGE_MANAGERS = ["pnpm", "npm", "yarn", "bun"] as const
type PackageManager = (typeof PACKAGE_MANAGERS)[number]

const APPLY_MODES = [
  {
    value: "full",
    title: "Full preset",
    description:
      "Everything from the preset, including components, theme, and fonts.",
    flag: null,
    label: "full preset",
  },
  {
    value: "theme",
    title: "Theme only",
    description:
      "Theme tokens only, like colors, radii, and shadows. Components stay as they are.",
    flag: "--only theme",
    label: "--only theme",
  },
  {
    value: "font",
    title: "Fonts only",
    description:
      "Only preset fonts for body and headings. Components stay as they are.",
    flag: "--only font",
    label: "--only font",
  },
] as const
type ApplyMode = (typeof APPLY_MODES)[number]["value"]
type ProjectFormTab = "new-project" | "existing-project" | "theme"
type CopyTarget = "command" | "apply" | "theme"
type ThemeCssVars = NonNullable<
  ReturnType<typeof buildThemeForPreset>["cssVars"]
>

function formatCssVarsRule(selector: string, cssVars?: Record<string, string>) {
  const declarations = Object.entries(cssVars ?? {})
    .map(([key, value]) => `  --${key}: ${value};`)
    .join("\n")

  return `${selector} {\n${declarations}\n}`
}

function formatThemeCss(cssVars: ThemeCssVars) {
  return [
    formatCssVarsRule(":root", cssVars.light),
    formatCssVarsRule(".dark", cssVars.dark),
  ].join("\n\n")
}

export function ProjectForm({
  className,
}: React.ComponentProps<typeof Button>) {
  const [open, setOpen] = React.useState(false)
  const [params, setParams] = useDesignSystemSearchParams()
  const presetCode = usePresetCode()
  const [config, setConfig] = useConfig()
  const [copiedTarget, setCopiedTarget] = React.useState<CopyTarget | null>(
    null
  )
  const [applyMode, setApplyMode] = React.useState<ApplyMode>("full")
  const [activeTab, setActiveTab] =
    React.useState<ProjectFormTab>("new-project")

  const packageManager = (config.packageManager || "pnpm") as PackageManager
  const framework = React.useMemo(
    () => getFramework(params.template ?? "next"),
    [params.template]
  )
  const isMonorepo = React.useMemo(
    () => params.template?.endsWith("-monorepo") ?? false,
    [params.template]
  )

  const hasMonorepo = !NO_MONOREPO_FRAMEWORKS.includes(
    framework as (typeof NO_MONOREPO_FRAMEWORKS)[number]
  )

  const commands = React.useMemo(() => {
    const presetFlag = ` --preset ${presetCode}`
    const baseFlag =
      params.base !== DEFAULT_CONFIG.base ? ` --base ${params.base}` : ""
    const templateFlag = ` --template ${framework}`
    const monorepoFlag = isMonorepo ? " --monorepo" : ""
    const rtlFlag = params.rtl ? " --rtl" : ""
    const pointerFlag = params.pointer ? " --pointer" : ""
    const flags = `${presetFlag}${baseFlag}${templateFlag}${monorepoFlag}${rtlFlag}${pointerFlag}`

    return IS_LOCAL_DEV
      ? {
          pnpm: `shadcn init${flags}`,
          npm: `shadcn init${flags}`,
          yarn: `shadcn init${flags}`,
          bun: `shadcn init${flags}`,
        }
      : {
          pnpm: `pnpm dlx shadcn${SHADCN_VERSION} init${flags}`,
          npm: `npx shadcn${SHADCN_VERSION} init${flags}`,
          yarn: `yarn dlx shadcn${SHADCN_VERSION} init${flags}`,
          bun: `bunx --bun shadcn${SHADCN_VERSION} init${flags}`,
        }
  }, [
    framework,
    isMonorepo,
    params.base,
    params.pointer,
    params.rtl,
    presetCode,
  ])

  const command = commands[packageManager]

  const applyCommands = React.useMemo(() => {
    const presetFlag = ` --preset ${presetCode}`
    const onlyFlag =
      applyMode === "theme"
        ? " --only theme"
        : applyMode === "font"
          ? " --only font"
          : ""
    const flags = `${presetFlag}${onlyFlag}`

    return IS_LOCAL_DEV
      ? {
          pnpm: `shadcn apply${flags}`,
          npm: `shadcn apply${flags}`,
          yarn: `shadcn apply${flags}`,
          bun: `shadcn apply${flags}`,
        }
      : {
          pnpm: `pnpm dlx shadcn${SHADCN_VERSION} apply${flags}`,
          npm: `npx shadcn${SHADCN_VERSION} apply${flags}`,
          yarn: `yarn dlx shadcn${SHADCN_VERSION} apply${flags}`,
          bun: `bunx --bun shadcn${SHADCN_VERSION} apply${flags}`,
        }
  }, [applyMode, presetCode])

  const applyCommand = applyCommands[packageManager]
  const themeConfig = React.useMemo<DesignSystemConfig>(() => {
    const isRadiusLocked = params.style === "lyra" || params.style === "sera"

    return {
      ...DEFAULT_CONFIG,
      base: params.base,
      style: params.style,
      baseColor: params.baseColor,
      theme: params.theme,
      chartColor: params.chartColor,
      iconLibrary: params.iconLibrary,
      font: params.font,
      fontHeading: params.fontHeading,
      menuAccent: params.menuAccent,
      menuColor: params.menuColor,
      radius: isRadiusLocked ? "none" : params.radius,
      template: params.template,
      rtl: params.rtl,
      pointer: params.pointer,
    }
  }, [
    params.base,
    params.baseColor,
    params.chartColor,
    params.font,
    params.fontHeading,
    params.iconLibrary,
    params.menuAccent,
    params.menuColor,
    params.pointer,
    params.radius,
    params.rtl,
    params.style,
    params.template,
    params.theme,
  ])

  const themeCss = React.useMemo(() => {
    const theme = buildThemeForPreset(themeConfig)

    if (!theme.cssVars) {
      return ""
    }

    return formatThemeCss(theme.cssVars)
  }, [themeConfig])

  React.useEffect(() => {
    if (copiedTarget) {
      const timer = setTimeout(() => setCopiedTarget(null), 2000)
      return () => clearTimeout(timer)
    }
  }, [copiedTarget])

  const handleCopy = React.useCallback(() => {
    const properties: Record<string, string> = {
      command,
    }
    if (params.template) {
      properties.template = params.template
    }
    copyToClipboardWithMeta(command, {
      name: "copy_npm_command",
      properties,
    })
    setCopiedTarget("command")
  }, [command, params.template])

  const handleCopyApply = React.useCallback(() => {
    copyToClipboardWithMeta(applyCommand, {
      name: "copy_apply_command",
      properties: {
        command: applyCommand,
        applyMode,
      },
    })
    setCopiedTarget("apply")
  }, [applyCommand, applyMode])

  const handleCopyTheme = React.useCallback(() => {
    copyToClipboardWithMeta(themeCss, {
      name: "copy_theme_code",
      properties: {
        preset: presetCode,
        baseColor: params.baseColor,
        theme: params.theme,
        format: "css",
      },
    })
    setCopiedTarget("theme")
  }, [params.baseColor, params.theme, presetCode, themeCss])

  return (
    <Dialog open={open} onOpenChange={setOpen}>
      <DialogTrigger render={<Button className={cn(className)} />}>
        Get Code
      </DialogTrigger>
      <DialogContent className="dark top-[64px] no-scrollbar flex max-h-[calc(100svh-2rem)] translate-y-0 flex-col rounded-2xl p-0 shadow-xl **:data-[slot=dialog-close]:top-4.5 **:data-[slot=dialog-close]:right-4 **:data-[slot=field-separator]:h-2 sm:max-w-md">
        <div className="flex min-w-0 flex-1 flex-col gap-0 overflow-hidden rounded-2xl">
          <DialogHeader className="border-b px-6 py-5">
            <ToggleGroup
              value={[activeTab]}
              onValueChange={(values) =>
                setActiveTab((values[0] as typeof activeTab) ?? "new-project")
              }
              aria-label="Project type"
              spacing={2}
              className="**:data-[slot=toggle-group-item]:data-pressed:bg-neutral-700/70"
            >
              <ToggleGroupItem value="new-project">New Project</ToggleGroupItem>
              <ToggleGroupItem value="existing-project">
                Existing Project
              </ToggleGroupItem>
              <ToggleGroupItem value="theme">Theme</ToggleGroupItem>
            </ToggleGroup>
          </DialogHeader>
          {activeTab === "new-project" && (
            <div className="no-scrollbar overflow-y-auto">
              <FieldGroup className="px-6 py-4">
                <Field className="gap-3">
                  <FieldLabel>Template</FieldLabel>
                  <TemplateGrid
                    template={params.template}
                    setParams={setParams}
                  />
                </Field>
                <FieldSeparator className="-mx-6" />
                <Field>
                  <FieldLabel>Base</FieldLabel>
                  <BaseGrid base={params.base} setParams={setParams} />
                </Field>
                <FieldSeparator className="-mx-6" />
                <FieldSet>
                  <FieldLegend variant="label" className="sr-only">
                    Options
                  </FieldLegend>
                  <Field orientation="horizontal">
                    <FieldLabel htmlFor="pointer">
                      <HugeiconsIcon
                        icon={HandPointingRight04Icon}
                        className="size-4 -rotate-90"
                      />
                      Use pointer on buttons
                    </FieldLabel>
                    <Switch
                      id="pointer"
                      checked={params.pointer}
                      onCheckedChange={(checked) =>
                        setParams({ pointer: checked === true })
                      }
                    />
                  </Field>
                  <FieldSeparator className="-mx-6" />
                  <Field
                    orientation="horizontal"
                    data-disabled={hasMonorepo ? undefined : "true"}
                  >
                    <FieldLabel htmlFor="monorepo">
                      <span
                        className="size-4 text-neutral-100 [&_svg]:size-4 [&_svg]:fill-current"
                        dangerouslySetInnerHTML={{
                          __html: TURBOREPO_LOGO,
                        }}
                      />
                      Create a monorepo
                    </FieldLabel>
                    <Switch
                      id="monorepo"
                      checked={params.template?.endsWith("-monorepo") ?? false}
                      disabled={!hasMonorepo}
                      onCheckedChange={(checked) => {
                        const framework = getFramework(
                          params.template ?? "next"
                        )
                        setParams({
                          template: getTemplateValue(
                            framework,
                            checked === true
                          ) as typeof params.template,
                        })
                      }}
                    />
                  </Field>
                  <FieldSeparator className="-mx-6" />
                  <Field orientation="horizontal">
                    <FieldLabel htmlFor="rtl">
                      <HugeiconsIcon icon={Globe02Icon} className="size-4" />
                      Enable RTL support
                    </FieldLabel>
                    <Switch
                      id="rtl"
                      checked={params.rtl}
                      onCheckedChange={(checked) =>
                        setParams({ rtl: checked === true })
                      }
                    />
                  </Field>
                </FieldSet>
              </FieldGroup>
              <DialogFooter className="m-0 min-w-0 p-6">
                <div className="flex w-full min-w-0 flex-col gap-3">
                  <Tabs
                    value={packageManager}
                    onValueChange={(value) => {
                      setConfig((prev) => ({
                        ...prev,
                        packageManager: value as PackageManager,
                      }))
                    }}
                    className="min-w-0 gap-0 overflow-hidden rounded-xl border-0 ring-1 ring-border"
                  >
                    <div className="flex items-center gap-2 py-1 pr-1.5 pl-1">
                      <TabsList className="bg-transparent font-mono">
                        {PACKAGE_MANAGERS.map((manager) => {
                          return (
                            <TabsTrigger
                              key={manager}
                              value={manager}
                              className="py-0 leading-none data-[state=active]:shadow-none"
                            >
                              {manager}
                            </TabsTrigger>
                          )
                        })}
                      </TabsList>
                      <Button
                        size="icon-sm"
                        variant="ghost"
                        className="ml-auto"
                        onClick={handleCopy}
                      >
                        {copiedTarget === "command" ? (
                          <HugeiconsIcon icon={Tick02Icon} />
                        ) : (
                          <HugeiconsIcon icon={Copy01Icon} />
                        )}
                        <span className="sr-only">Copy command</span>
                      </Button>
                    </div>
                    {Object.entries(commands).map(([key, cmd]) => {
                      return (
                        <TabsContent key={key} value={key}>
                          <div className="relative overflow-hidden border-t bg-popover p-3">
                            <div className="no-scrollbar overflow-x-auto">
                              <code className="font-mono text-sm whitespace-nowrap">
                                {cmd}
                              </code>
                            </div>
                          </div>
                        </TabsContent>
                      )
                    })}
                  </Tabs>
                  <Button onClick={handleCopy} className="h-9 w-full">
                    {copiedTarget === "command" ? "Copied" : "Copy Command"}
                  </Button>
                </div>
              </DialogFooter>
            </div>
          )}
          {activeTab === "existing-project" && (
            <div className="no-scrollbar overflow-y-auto">
              <FieldGroup className="px-6 py-4">
                <FieldSet className="gap-3">
                  <FieldLegend variant="label">Apply Preset</FieldLegend>
                  <FieldDescription>
                    Pick which parts of the preset to apply.
                  </FieldDescription>
                  <ApplyModeGrid mode={applyMode} setMode={setApplyMode} />
                </FieldSet>
              </FieldGroup>
              <DialogFooter className="m-0 min-w-0 p-6">
                <div className="flex w-full min-w-0 flex-col gap-3">
                  <Tabs
                    value={packageManager}
                    onValueChange={(value) => {
                      setConfig((prev) => ({
                        ...prev,
                        packageManager: value as PackageManager,
                      }))
                    }}
                    className="min-w-0 gap-0 overflow-hidden rounded-xl border-0 ring-1 ring-border"
                  >
                    <div className="flex items-center gap-2 py-1 pr-1.5 pl-1">
                      <TabsList className="bg-transparent font-mono">
                        {PACKAGE_MANAGERS.map((manager) => {
                          return (
                            <TabsTrigger
                              key={manager}
                              value={manager}
                              className="py-0 leading-none data-[state=active]:shadow-none"
                            >
                              {manager}
                            </TabsTrigger>
                          )
                        })}
                      </TabsList>
                      <Button
                        size="icon-sm"
                        variant="ghost"
                        className="ml-auto"
                        onClick={handleCopyApply}
                      >
                        {copiedTarget === "apply" ? (
                          <HugeiconsIcon icon={Tick02Icon} />
                        ) : (
                          <HugeiconsIcon icon={Copy01Icon} />
                        )}
                        <span className="sr-only">Copy command</span>
                      </Button>
                    </div>
                    {Object.entries(applyCommands).map(([key, cmd]) => {
                      return (
                        <TabsContent key={key} value={key}>
                          <div className="relative overflow-hidden border-t bg-popover p-3">
                            <div className="no-scrollbar overflow-x-auto">
                              <code className="font-mono text-sm whitespace-nowrap">
                                {cmd}
                              </code>
                            </div>
                          </div>
                        </TabsContent>
                      )
                    })}
                  </Tabs>
                  <Button onClick={handleCopyApply} className="h-9 w-full">
                    {copiedTarget === "apply" ? "Copied" : "Copy Command"}
                  </Button>
                </div>
              </DialogFooter>
            </div>
          )}
          {activeTab === "theme" && (
            <div className="no-scrollbar overflow-y-auto">
              <FieldGroup className="min-w-0 px-6 py-4">
                <FieldSet className="min-w-0 gap-3">
                  <FieldLegend variant="label">Theme Tokens</FieldLegend>
                  <FieldDescription>
                    Copy the CSS variables for this preset.
                  </FieldDescription>
                  <div className="w-full min-w-0 overflow-hidden rounded-xl border-0 ring-1 ring-border">
                    <div className="flex items-center gap-2 py-1 pr-1.5 pl-3">
                      <div className="min-w-0 truncate font-mono text-sm text-muted-foreground">
                        globals.css
                      </div>
                      <Button
                        size="icon-sm"
                        variant="ghost"
                        className="ml-auto"
                        onClick={handleCopyTheme}
                      >
                        {copiedTarget === "theme" ? (
                          <HugeiconsIcon icon={Tick02Icon} />
                        ) : (
                          <HugeiconsIcon icon={Copy01Icon} />
                        )}
                        <span className="sr-only">Copy theme</span>
                      </Button>
                    </div>
                    <div className="relative no-scrollbar max-h-[45svh] overflow-auto border-t bg-popover p-3">
                      <pre className="min-w-max font-mono leading-normal whitespace-pre">
                        <code>{themeCss}</code>
                      </pre>
                    </div>
                  </div>
                </FieldSet>
              </FieldGroup>
              <DialogFooter className="m-0 min-w-0 p-6">
                <Button onClick={handleCopyTheme} className="h-9 w-full">
                  {copiedTarget === "theme" ? "Copied" : "Copy Theme"}
                </Button>
              </DialogFooter>
            </div>
          )}
        </div>
      </DialogContent>
    </Dialog>
  )
}

const TemplateGrid = React.memo(function TemplateGrid({
  template,
  setParams,
}: {
  template: DesignSystemSearchParams["template"]
  setParams: ReturnType<typeof useDesignSystemSearchParams>[1]
}) {
  const isMonorepo = template?.endsWith("-monorepo") ?? false
  const framework = getFramework(template ?? "next")

  const handleTemplateChange = React.useCallback(
    (value: string) => {
      setParams({
        template: getTemplateValue(
          value,
          isMonorepo
        ) as DesignSystemSearchParams["template"],
      })
    },
    [isMonorepo, setParams]
  )

  return (
    <RadioGroup
      value={framework}
      onValueChange={handleTemplateChange}
      className="grid grid-cols-2 gap-2"
    >
      {TEMPLATES.map((item) => (
        <FieldLabel
          key={item.value}
          htmlFor={`template-${item.value}`}
          className="block w-full"
        >
          <Field
            orientation="horizontal"
            className="w-full rounded-md transition-colors duration-150 hover:bg-neutral-700/45"
          >
            <FieldContent className="flex flex-row items-center gap-2 px-2.5 py-1.5">
              <div
                className="size-4 text-neutral-100 [&_svg]:size-4 *:[svg]:text-neutral-100!"
                dangerouslySetInnerHTML={{
                  __html: item.logo,
                }}
              ></div>
              <FieldTitle>{item.title}</FieldTitle>
            </FieldContent>
            <RadioGroupItem
              value={item.value}
              id={`template-${item.value}`}
              className="sr-only absolute"
            />
          </Field>
        </FieldLabel>
      ))}
    </RadioGroup>
  )
})

const ApplyModeGrid = React.memo(function ApplyModeGrid({
  mode,
  setMode,
}: {
  mode: ApplyMode
  setMode: (mode: ApplyMode) => void
}) {
  return (
    <RadioGroup
      value={mode}
      onValueChange={(value) => setMode(value as ApplyMode)}
      aria-label="Apply"
    >
      {APPLY_MODES.map((option) => (
        <FieldLabel key={option.value} htmlFor={`apply-${option.value}`}>
          <Field orientation="horizontal">
            <RadioGroupItem value={option.value} id={`apply-${option.value}`} />
            <FieldContent>
              <FieldTitle>{option.title}</FieldTitle>
              <FieldDescription>{option.description}</FieldDescription>
            </FieldContent>
          </Field>
        </FieldLabel>
      ))}
    </RadioGroup>
  )
})

const BaseGrid = React.memo(function BaseGrid({
  base,
  setParams,
}: {
  base: DesignSystemSearchParams["base"]
  setParams: ReturnType<typeof useDesignSystemSearchParams>[1]
}) {
  const handleBaseChange = React.useCallback(
    (value: string) => {
      setParams({ base: value as BaseName })
    },
    [setParams]
  )

  return (
    <RadioGroup
      value={base}
      onValueChange={handleBaseChange}
      aria-label="Base"
      className="grid grid-cols-1 gap-2 sm:grid-cols-3"
    >
      {BASES.map((item) => (
        <FieldLabel
          key={item.name}
          htmlFor={`base-${item.name}`}
          className="block w-full"
        >
          <Field
            orientation="horizontal"
            className="w-full rounded-md transition-colors duration-150 hover:bg-neutral-700/45"
          >
            <FieldContent className="flex flex-row items-center gap-2 py-1">
              <div
                className="size-4 shrink-0 text-neutral-100 [&_svg]:size-4 *:[svg]:text-neutral-100!"
                dangerouslySetInnerHTML={{
                  __html: item.meta?.logo ?? "",
                }}
              />
              <FieldTitle className="whitespace-nowrap">
                {item.title}
              </FieldTitle>
            </FieldContent>
            <RadioGroupItem
              value={item.name}
              id={`base-${item.name}`}
              className="sr-only absolute"
            />
          </Field>
        </FieldLabel>
      ))}
    </RadioGroup>
  )
})
