import { useTranslation } from "react-i18next"

import type { ChannelConfig } from "@/api/channels"
import {
  type ArrayFieldFlusher,
  ChannelArrayListField,
} from "@/components/channels/channel-array-list-field"
import {
  asStringArray,
  parseAllowFromInput,
} from "@/components/channels/channel-array-utils"
import {
  getSecretInputPlaceholder,
  isSecretField,
} from "@/components/channels/channel-config-fields"
import { Field, KeyInput, SwitchCardField } from "@/components/shared-form"
import { Card, CardContent } from "@/components/ui/card"
import { Input } from "@/components/ui/input"

interface GenericFormProps {
  config: ChannelConfig
  onChange: (key: string, value: unknown) => void
  configuredSecrets?: string[]
  hiddenKeys?: string[]
  requiredKeys?: string[]
  fieldErrors?: Record<string, string>
  registerArrayFieldFlusher?: (
    fieldPath: string,
    flusher: ArrayFieldFlusher | null,
  ) => void
  arrayFieldResetVersion?: number
}

// Fields to skip in the generic form (handled by enabled toggle or internal).
const SKIP_FIELDS = new Set(["enabled", "reasoning_channel_id"])

// Fields that are objects/nested — show as JSON or skip.
const OBJECT_FIELDS = new Set([
  "group_trigger",
  "typing",
  "placeholder",
  "allow_token_query",
  "allow_from",
  "allow_origins",
  "groups",
])

function formatLabel(key: string): string {
  return key
    .split("_")
    .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
    .join(" ")
}

function formatSentenceFieldName(key: string): string {
  const label = formatLabel(key)
  return label.charAt(0).toLowerCase() + label.slice(1)
}

function asString(value: unknown): string {
  return typeof value === "string" ? value : ""
}

function asRecord(value: unknown): Record<string, unknown> {
  if (value && typeof value === "object" && !Array.isArray(value)) {
    return value as Record<string, unknown>
  }
  return {}
}

function asBool(value: unknown): boolean {
  return value === true
}

export function GenericForm({
  config,
  onChange,
  configuredSecrets = [],
  hiddenKeys = [],
  requiredKeys = [],
  fieldErrors = {},
  registerArrayFieldFlusher,
  arrayFieldResetVersion,
}: GenericFormProps) {
  const { t } = useTranslation()
  const hiddenFieldSet = new Set(hiddenKeys)
  const requiredFieldSet = new Set(requiredKeys)
  const groupTriggerConfig = asRecord(config.group_trigger)
  const typingConfig = asRecord(config.typing)
  const placeholderConfig = asRecord(config.placeholder)
  const placeholderEnabled = asBool(placeholderConfig.enabled)

  const rawFields = Object.keys(config).filter(
    (k) =>
      !k.startsWith("_") &&
      !SKIP_FIELDS.has(k) &&
      !OBJECT_FIELDS.has(k) &&
      !hiddenFieldSet.has(k),
  )

  const buildHint = (key: string): string => {
    const descriptions: Record<string, string> = {
      ws_url: t("channels.form.desc.wsUrl"),
      reconnect_interval: t("channels.form.desc.reconnectInterval"),
      bridge_url: t("channels.form.desc.bridgeUrl"),
      session_store_path: t("channels.form.desc.sessionStorePath"),
      use_native: t("channels.form.desc.useNative"),
      host: t("channels.form.desc.host"),
      port: t("channels.form.desc.port"),
      homeserver: t("channels.form.desc.homeserver"),
      user_id: t("channels.form.desc.userId"),
      device_id: t("channels.form.desc.deviceId"),
      join_on_invite: t("channels.form.desc.joinOnInvite"),
      app_id: t("channels.form.desc.appId"),
      client_id: t("channels.form.desc.clientId"),
      corp_id: t("channels.form.desc.corpId"),
      bot_id: t("channels.form.desc.appId"),
      websocket_url: t("channels.form.desc.wsUrl"),
      dm_policy: t("channels.form.desc.genericField", { field: "DM policy" }),
      group_policy: t("channels.form.desc.genericField", {
        field: "group policy",
      }),
      group_allow_from: t("channels.form.desc.allowFrom"),
      send_thinking_message: t("channels.form.desc.genericField", {
        field: "thinking message behavior",
      }),
      agent_id: t("channels.form.desc.agentId"),
      webhook_url: t("channels.form.desc.webhookUrl"),
      webhook_host: t("channels.form.desc.webhookHost"),
      webhook_port: t("channels.form.desc.webhookPort"),
      webhook_path: t("channels.form.desc.webhookPath"),
      reply_timeout: t("channels.form.desc.replyTimeout"),
      max_steps: t("channels.form.desc.maxSteps"),
      welcome_message: t("channels.form.desc.welcomeMessage"),
      allow_token_query: t("channels.form.desc.allowTokenQuery"),
      ping_interval: t("channels.form.desc.pingInterval"),
      read_timeout: t("channels.form.desc.readTimeout"),
      write_timeout: t("channels.form.desc.writeTimeout"),
      max_connections: t("channels.form.desc.maxConnections"),
      server: t("channels.form.desc.server"),
      tls: t("channels.form.desc.tls"),
      nick: t("channels.form.desc.nick"),
      user: t("channels.form.desc.user"),
      real_name: t("channels.form.desc.realName"),
      channels: t("channels.form.desc.channels"),
      request_caps: t("channels.form.desc.requestCaps"),
      max_base64_file_size_mib: t("channels.form.desc.maxBase64FileSizeMiB"),
    }
    return (
      descriptions[key] ??
      t("channels.form.desc.genericField", {
        field: formatSentenceFieldName(key),
      })
    )
  }

  const renderField = (key: string) => {
    const isRequired = requiredFieldSet.has(key)
    if (isSecretField(key)) {
      const editKey = `_${key}`
      return (
        <Field
          key={key}
          label={formatLabel(key)}
          required={isRequired}
          hint={buildHint(key)}
          error={fieldErrors[key]}
        >
          <KeyInput
            value={asString(config[editKey])}
            onChange={(v) => onChange(editKey, v)}
            placeholder={getSecretInputPlaceholder(
              configuredSecrets,
              key,
              t("channels.field.secretHintSet"),
              t("channels.field.secretPlaceholder"),
            )}
          />
        </Field>
      )
    }

    const value = config[key]
    if (typeof value === "boolean") {
      return (
        <SwitchCardField
          key={key}
          label={formatLabel(key)}
          hint={buildHint(key)}
          error={fieldErrors[key]}
          checked={value}
          onCheckedChange={(checked) => onChange(key, checked)}
          ariaLabel={formatLabel(key)}
        />
      )
    }

    if (Array.isArray(value)) {
      return (
        <ChannelArrayListField
          key={key}
          label={formatLabel(key)}
          required={isRequired}
          hint={buildHint(key)}
          error={fieldErrors[key]}
          value={asStringArray(value)}
          onChange={(nextValue) => onChange(key, nextValue)}
          fieldPath={key}
          registerFlusher={registerArrayFieldFlusher}
          resetVersion={arrayFieldResetVersion}
        />
      )
    }

    return (
      <Field
        key={key}
        label={formatLabel(key)}
        required={isRequired}
        hint={buildHint(key)}
        error={fieldErrors[key]}
      >
        <Input
          value={String(value ?? "")}
          onChange={(e) => {
            const v = e.target.value
            if (typeof config[key] === "number") {
              onChange(key, v === "" ? 0 : Number(v))
            } else {
              onChange(key, v)
            }
          }}
        />
      </Field>
    )
  }

  const isBasicField = (key: string) => {
    if (requiredFieldSet.has(key)) return true
    if (
      key.endsWith("id") ||
      key.endsWith("token") ||
      key.endsWith("secret") ||
      key.endsWith("url") ||
      key === "server" ||
      key === "host" ||
      key === "port"
    ) {
      return true
    }
    return false
  }

  const basicFields = rawFields.filter(isBasicField)
  const advancedFields = rawFields.filter((key) => !isBasicField(key))

  const hasAdvancedContent =
    advancedFields.length > 0 ||
    (config.allow_from !== undefined && !hiddenFieldSet.has("allow_from")) ||
    (config.allow_origins !== undefined &&
      !hiddenFieldSet.has("allow_origins")) ||
    (config.allow_token_query !== undefined &&
      !hiddenFieldSet.has("allow_token_query")) ||
    (config.group_trigger !== undefined &&
      !hiddenFieldSet.has("group_trigger")) ||
    (config.typing !== undefined && !hiddenFieldSet.has("typing")) ||
    (config.placeholder !== undefined && !hiddenFieldSet.has("placeholder"))

  return (
    <div className="space-y-6">
      {basicFields.length > 0 && (
        <Card className="shadow-sm">
          <CardContent className="divide-border/60 divide-y px-6 py-0 [&>div]:py-5">
            {basicFields.map(renderField)}
          </CardContent>
        </Card>
      )}

      {hasAdvancedContent && (
        <Card className="shadow-sm">
          <CardContent className="divide-border/60 divide-y px-6 py-0 [&>div]:py-5">
            {advancedFields.map(renderField)}

            {config.allow_from !== undefined &&
              !hiddenFieldSet.has("allow_from") && (
                <ChannelArrayListField
                  label={t("channels.field.allowFrom")}
                  hint={t("channels.form.desc.allowFrom")}
                  value={asStringArray(config.allow_from)}
                  onChange={(value) => onChange("allow_from", value)}
                  placeholder={t("channels.field.allowFromPlaceholder")}
                  parser={parseAllowFromInput}
                  fieldPath="allow_from"
                  registerFlusher={registerArrayFieldFlusher}
                  resetVersion={arrayFieldResetVersion}
                />
              )}

            {config.allow_origins !== undefined &&
              !hiddenFieldSet.has("allow_origins") && (
                <ChannelArrayListField
                  label={t("channels.field.allowOrigins")}
                  hint={t("channels.form.desc.allowOrigins")}
                  value={asStringArray(config.allow_origins)}
                  onChange={(value) => onChange("allow_origins", value)}
                  placeholder={t("channels.field.allowOriginsPlaceholder")}
                  fieldPath="allow_origins"
                  registerFlusher={registerArrayFieldFlusher}
                  resetVersion={arrayFieldResetVersion}
                />
              )}

            {config.allow_token_query !== undefined &&
              !hiddenFieldSet.has("allow_token_query") && (
                <div>
                  <SwitchCardField
                    label={formatLabel("allow_token_query")}
                    hint={buildHint("allow_token_query")}
                    checked={asBool(config.allow_token_query)}
                    onCheckedChange={(checked) =>
                      onChange("allow_token_query", checked)
                    }
                    ariaLabel={formatLabel("allow_token_query")}
                  />
                </div>
              )}

            {config.group_trigger !== undefined &&
              !hiddenFieldSet.has("group_trigger") && (
                <>
                  <div>
                    <SwitchCardField
                      label={t("channels.field.groupTriggerMentionOnly")}
                      hint={t("channels.form.desc.groupTriggerMentionOnly")}
                      checked={asBool(groupTriggerConfig.mention_only)}
                      onCheckedChange={(checked) =>
                        onChange("group_trigger", {
                          ...groupTriggerConfig,
                          mention_only: checked,
                        })
                      }
                      ariaLabel={t("channels.field.groupTriggerMentionOnly")}
                    />
                  </div>

                  <ChannelArrayListField
                    label={t("channels.field.groupTriggerPrefixes")}
                    hint={t("channels.form.desc.groupTriggerPrefixes")}
                    value={asStringArray(groupTriggerConfig.prefixes)}
                    onChange={(value) =>
                      onChange("group_trigger", {
                        ...groupTriggerConfig,
                        prefixes: value,
                      })
                    }
                    placeholder={t("channels.field.groupTriggerPrefixes")}
                    fieldPath="group_trigger.prefixes"
                    registerFlusher={registerArrayFieldFlusher}
                    resetVersion={arrayFieldResetVersion}
                  />
                </>
              )}

            {config.typing !== undefined && !hiddenFieldSet.has("typing") && (
              <div>
                <SwitchCardField
                  label={t("channels.field.typingEnabled")}
                  hint={t("channels.form.desc.typingEnabled")}
                  checked={asBool(typingConfig.enabled)}
                  onCheckedChange={(checked) =>
                    onChange("typing", { ...typingConfig, enabled: checked })
                  }
                  ariaLabel={t("channels.field.typingEnabled")}
                />
              </div>
            )}

            {config.placeholder !== undefined &&
              !hiddenFieldSet.has("placeholder") && (
                <div>
                  <SwitchCardField
                    label={t("channels.field.placeholderEnabled")}
                    hint={t("channels.form.desc.placeholderEnabled")}
                    checked={placeholderEnabled}
                    onCheckedChange={(checked) =>
                      onChange("placeholder", {
                        ...placeholderConfig,
                        enabled: checked,
                      })
                    }
                    ariaLabel={t("channels.field.placeholderEnabled")}
                  >
                    {placeholderEnabled && (
                      <div className="space-y-1">
                        <Input
                          value={asString(placeholderConfig.text)}
                          onChange={(e) =>
                            onChange("placeholder", {
                              ...placeholderConfig,
                              text: e.target.value,
                            })
                          }
                          placeholder={t("channels.field.placeholderText")}
                          aria-label={t("channels.field.placeholderText")}
                        />
                      </div>
                    )}
                  </SwitchCardField>
                </div>
              )}
          </CardContent>
        </Card>
      )}
    </div>
  )
}
