import { format } from "date-fns";
import React from "react";

import { Input as DSInput } from "@/components/atoms/Input/Input";
import { Select as DSSelect } from "@/components/atoms/Select/Select";
import { MultiToggle } from "@/components/molecules/MultiToggle/MultiToggle";
// Removed shadcn Select usage in favor of DS Select for time picker
import { Button } from "@/components/atoms/Button/Button";
import { FileInput } from "@/components/atoms/FileInput/FileInput";
import { Switch } from "@/components/atoms/Switch/Switch";
import { GoogleDrivePickerInput } from "@/components/contextual/GoogleDrivePicker/GoogleDrivePickerInput";
import { InformationTooltip } from "@/components/molecules/InformationTooltip/InformationTooltip";
import { TimePicker } from "@/components/molecules/TimePicker/TimePicker";
import {
  BlockIOObjectSubSchema,
  BlockIOSubSchema,
  BlockIOTableSubSchema,
  DataType,
  determineDataType,
  TableRow,
} from "@/lib/autogpt-server-api/types";
import { PlusIcon, XIcon } from "@phosphor-icons/react";
import { useRunAgentInputs } from "./useRunAgentInputs";

/**
 * A generic prop structure for the TypeBasedInput.
 *
 * onChange expects an event-like object with e.target.value so the parent
 * can do something like setInputValues(e.target.value).
 */
interface Props {
  schema: BlockIOSubSchema;
  value?: any;
  placeholder?: string;
  onChange: (value: any) => void;
  readOnly?: boolean;
}

/**
 * A generic, data-type-based input component that uses Shadcn UI.
 * It inspects the schema via `determineDataType` and renders
 * the correct UI component.
 */
export function RunAgentInputs({
  schema,
  value,
  placeholder,
  onChange,
  readOnly = false,
  ...props
}: Props & React.HTMLAttributes<HTMLElement>) {
  const { handleUploadFile, uploadProgress } = useRunAgentInputs();

  const dataType = determineDataType(schema);

  const baseId = String(schema.title ?? "input")
    .replace(/\s+/g, "-")
    .toLowerCase();

  let innerInputElement: React.ReactNode = null;
  switch (dataType) {
    case DataType.NUMBER:
      innerInputElement = (
        <DSInput
          id={`${baseId}-number`}
          label={schema.title ?? placeholder ?? "Number"}
          hideLabel
          type="number"
          value={value ?? ""}
          placeholder={placeholder || "Enter number"}
          onChange={(e) => {
            const v = (e.target as HTMLInputElement).value;
            if (!v || v.trim() === "") {
              onChange(undefined);
            } else {
              const numValue = Number(v);
              onChange(isNaN(numValue) ? undefined : numValue);
            }
          }}
          {...props}
        />
      );
      break;

    case DataType.LONG_TEXT:
      innerInputElement = (
        <DSInput
          id={`${baseId}-textarea`}
          label={schema.title ?? placeholder ?? "Text"}
          hideLabel
          type="textarea"
          rows={3}
          value={value ?? ""}
          placeholder={placeholder || "Enter text"}
          onChange={(e) => onChange((e.target as HTMLInputElement).value)}
          {...props}
        />
      );
      break;

    case DataType.GOOGLE_DRIVE_PICKER: {
      const pickerSchema = schema as any;
      const config: import("@/lib/autogpt-server-api/types").GoogleDrivePickerConfig =
        pickerSchema.google_drive_picker_config || {};

      innerInputElement = (
        <GoogleDrivePickerInput
          config={config}
          value={value}
          onChange={onChange}
          className="w-full"
          showRemoveButton={!readOnly}
        />
      );
      break;
    }

    case DataType.BOOLEAN:
      innerInputElement = (
        <>
          <span className="text-sm text-gray-500">
            {placeholder || (value ? "Enabled" : "Disabled")}
          </span>
          <Switch
            className="ml-auto"
            checked={!!value}
            onCheckedChange={(checked: boolean) => onChange(checked)}
            {...props}
          />
        </>
      );
      break;

    case DataType.DATE: {
      function normalizeDateValue(val: any): Date | null {
        if (!val) return null;
        if (val instanceof Date) {
          return isNaN(val.getTime()) ? null : val;
        }
        if (typeof val === "string") {
          if (/^\d{4}-\d{2}-\d{2}$/.test(val)) {
            const [y, m, d] = val.split("-").map(Number);
            const date = new Date(y, m - 1, d);
            return isNaN(date.getTime()) ? null : date;
          }
          const date = new Date(val);
          return isNaN(date.getTime()) ? null : date;
        }
        return null;
      }

      const dateValue = normalizeDateValue(value);
      innerInputElement = (
        <DSInput
          id={`${baseId}-date`}
          label={schema.title ?? placeholder ?? "Date"}
          hideLabel
          type="date"
          value={dateValue ? format(dateValue, "yyyy-MM-dd") : ""}
          onChange={(e) => {
            const v = (e.target as HTMLInputElement).value;
            if (!v) onChange(undefined);
            else {
              const [y, m, d] = v.split("-").map(Number);
              onChange(new Date(y, m - 1, d));
            }
          }}
          placeholder={placeholder || "Pick a date"}
          {...props}
        />
      );
      break;
    }

    case DataType.TIME:
      innerInputElement = (
        <TimePicker value={value?.toString()} onChange={onChange} />
      );
      break;

    case DataType.DATE_TIME:
      innerInputElement = (
        <DSInput
          id={`${baseId}-datetime`}
          label={schema.title ?? placeholder ?? "Date time"}
          hideLabel
          type="datetime-local"
          value={value ?? ""}
          onChange={(e) => onChange((e.target as HTMLInputElement).value)}
          placeholder={placeholder || "Enter date and time"}
          {...props}
        />
      );
      break;

    case DataType.FILE:
      innerInputElement = (
        <FileInput
          value={value}
          placeholder={placeholder}
          onChange={onChange}
          onUploadFile={handleUploadFile}
          uploadProgress={uploadProgress}
          {...props}
        />
      );
      break;

    case DataType.SELECT:
      if (
        "enum" in schema &&
        Array.isArray(schema.enum) &&
        schema.enum.length > 0
      ) {
        innerInputElement = (
          <DSSelect
            id={`${baseId}-select`}
            label={schema.title ?? placeholder ?? "Select"}
            hideLabel
            value={value ?? ""}
            onValueChange={(val: string) => onChange(val)}
            placeholder={placeholder || "Select an option"}
            options={schema.enum
              .filter((opt) => opt)
              .map((opt) => ({ value: opt, label: String(opt) }))}
          />
        );
        break;
      }

    case DataType.MULTI_SELECT: {
      const _schema = schema as BlockIOObjectSubSchema;
      const allKeys = Object.keys(_schema.properties);
      const selectedValues = Object.entries(value || {})
        .filter(([_, v]) => v)
        .map(([k]) => k);

      innerInputElement = (
        <MultiToggle
          items={allKeys.map((key) => ({
            value: key,
            label: _schema.properties[key]?.title ?? key,
          }))}
          selectedValues={selectedValues}
          onChange={(values: string[]) =>
            onChange(
              Object.fromEntries(
                allKeys.map((opt) => [opt, values.includes(opt)]),
              ),
            )
          }
          className="nodrag"
          aria-label={schema.title}
        />
      );
      break;
    }

    case DataType.TABLE: {
      // Render a simple table UI for the run modal
      const tableSchema = schema as BlockIOTableSubSchema;
      const headers = tableSchema.items?.properties
        ? Object.keys(tableSchema.items.properties)
        : ["Column 1", "Column 2", "Column 3"];

      const tableData: TableRow[] = Array.isArray(value) ? value : [];

      const updateRow = (index: number, header: string, newValue: string) => {
        const newData = [...tableData];
        if (!newData[index]) {
          newData[index] = {};
        }
        newData[index][header] = newValue;
        onChange(newData);
      };

      const addRow = () => {
        const newRow: TableRow = {};
        headers.forEach((header) => {
          newRow[header] = "";
        });
        onChange([...tableData, newRow]);
      };

      const removeRow = (index: number) => {
        const newData = tableData.filter((_, i) => i !== index);
        onChange(newData);
      };

      innerInputElement = (
        <div className="w-full space-y-2">
          <div className="overflow-hidden rounded-md border">
            <table className="w-full text-sm">
              <thead>
                <tr className="bg-gray-50 dark:bg-gray-800">
                  {headers.map((header) => (
                    <th
                      key={header}
                      className="px-3 py-2 text-left font-medium"
                    >
                      {header}
                    </th>
                  ))}
                  <th className="w-10 px-3 py-2"></th>
                </tr>
              </thead>
              <tbody>
                {tableData.map((row, rowIndex) => (
                  <tr key={rowIndex} className="border-t dark:border-gray-700">
                    {headers.map((header) => (
                      <td key={header} className="px-3 py-1">
                        <input
                          type="text"
                          value={String(row[header] || "")}
                          onChange={(e) =>
                            updateRow(rowIndex, header, e.target.value)
                          }
                          className="w-full rounded border px-2 py-1 dark:border-gray-700 dark:bg-gray-900"
                          placeholder={`Enter ${header}`}
                        />
                      </td>
                    ))}
                    <td className="px-3 py-1">
                      <Button
                        type="button"
                        variant="ghost"
                        size="small"
                        onClick={() => removeRow(rowIndex)}
                        className="h-8 w-8 p-0"
                      >
                        <XIcon className="h-4 w-4" weight="bold" />
                      </Button>
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
          <Button
            type="button"
            variant="outline"
            size="small"
            onClick={addRow}
            className="w-full"
          >
            <PlusIcon className="mr-2 h-4 w-4" weight="bold" />
            Add Row
          </Button>
        </div>
      );
      break;
    }

    case DataType.SHORT_TEXT:
    default:
      innerInputElement = (
        <DSInput
          id={`${baseId}-text`}
          label={schema.title ?? placeholder ?? "Text"}
          hideLabel
          type="text"
          value={value ?? ""}
          onChange={(e) => onChange((e.target as HTMLInputElement).value)}
          placeholder={placeholder || "Enter text"}
          {...props}
        />
      );
  }

  return (
    <div className="flex w-full flex-col gap-0 space-y-2">
      <label className="large-medium flex items-center gap-1 font-medium">
        {schema.title || placeholder}
        <InformationTooltip description={schema.description} />
      </label>
      <div
        className="no-drag relative flex w-full"
        style={readOnly ? { pointerEvents: "none", opacity: 0.7 } : undefined}
      >
        {innerInputElement}
      </div>
    </div>
  );
}
