import { h as create, Fragment, VNode } from "preact";
import { Caption } from "./Caption.js";
import { Group } from "./Group.js";
import { InputAbsoluteTime } from "./InputAbsoluteTime.js";
import { InputAmount } from "./InputAmount.js";
import { InputArray } from "./InputArray.js";
import { InputChoiceHorizontal } from "./InputChoiceHorizontal.js";
import { InputChoiceStacked } from "./InputChoiceStacked.js";
import { InputFile } from "./InputFile.js";
import { InputInteger } from "./InputInteger.js";
import { InputSelectMultiple } from "./InputSelectMultiple.js";
import { InputSelectOne } from "./InputSelectOne.js";
import { InputText } from "./InputText.js";
import { InputTextArea } from "./InputTextArea.js";
import { InputToggle } from "./InputToggle.js";
import { Addon, StringConverter, UIFieldHandler } from "./FormProvider.js";
import { InternationalizationAPI, UIFieldElementDescription } from "../index.browser.js";
import { assertUnreachable, TranslatedString } from "@gnu-taler/taler-util";
import {UIFormFieldBaseConfig, UIFormElementConfig} from "./ui-form.js";
/**
 * Constrain the type with the ui props
 */
type FieldType<T extends object = any, K extends keyof T = any> = {
  group: Parameters<typeof Group>[0];
  caption: Parameters<typeof Caption>[0];
  array: Parameters<typeof InputArray<T, K>>[0];
  file: Parameters<typeof InputFile<T, K>>[0];
  selectOne: Parameters<typeof InputSelectOne<T, K>>[0];
  selectMultiple: Parameters<typeof InputSelectMultiple<T, K>>[0];
  text: Parameters<typeof InputText<T, K>>[0];
  textArea: Parameters<typeof InputTextArea<T, K>>[0];
  choiceStacked: Parameters<typeof InputChoiceStacked<T, K>>[0];
  choiceHorizontal: Parameters<typeof InputChoiceHorizontal<T, K>>[0];
  absoluteTimeText: Parameters<typeof InputAbsoluteTime<T, K>>[0];
  integer: Parameters<typeof InputInteger<T, K>>[0];
  toggle: Parameters<typeof InputToggle<T, K>>[0];
  amount: Parameters<typeof InputAmount<T, K>>[0];
};

/**
 * List all the form fields so typescript can type-check the form instance
 */
export type UIFormField =
  | { type: "group"; properties: FieldType["group"] }
  | { type: "caption"; properties: FieldType["caption"] }
  | { type: "array"; properties: FieldType["array"] }
  | { type: "file"; properties: FieldType["file"] }
  | { type: "amount"; properties: FieldType["amount"] }
  | { type: "selectOne"; properties: FieldType["selectOne"] }
  | {
      type: "selectMultiple";
      properties: FieldType["selectMultiple"];
    }
  | { type: "text"; properties: FieldType["text"] }
  | { type: "textArea"; properties: FieldType["textArea"] }
  | {
      type: "choiceStacked";
      properties: FieldType["choiceStacked"];
    }
  | {
      type: "choiceHorizontal";
      properties: FieldType["choiceHorizontal"];
    }
  | { type: "integer"; properties: FieldType["integer"] }
  | { type: "toggle"; properties: FieldType["toggle"] }
  | {
      type: "absoluteTimeText";
      properties: FieldType["absoluteTimeText"];
    };

type FieldComponentFunction<key extends keyof FieldType> = (
  props: FieldType[key],
) => VNode;

type UIFormFieldMap = {
  [key in keyof FieldType]: FieldComponentFunction<key>;
};

/**
 * Maps input type with component implementation
 */
const UIFormConfiguration: UIFormFieldMap = {
  group: Group,
  caption: Caption,
  //@ts-ignore
  array: InputArray,
  text: InputText,
  //@ts-ignore
  file: InputFile,
  textArea: InputTextArea,
  //@ts-ignore
  absoluteTimeText: InputAbsoluteTime,
  //@ts-ignore
  choiceStacked: InputChoiceStacked,
  //@ts-ignore
  choiceHorizontal: InputChoiceHorizontal,
  integer: InputInteger,
  //@ts-ignore
  selectOne: InputSelectOne,
  //@ts-ignore
  selectMultiple: InputSelectMultiple,
  //@ts-ignore
  toggle: InputToggle,
  //@ts-ignore
  amount: InputAmount,
};

export function RenderAllFieldsByUiConfig({
  fields,
}: {
  fields: UIFormField[];
}): VNode {
  return create(
    Fragment,
    {},
    fields.map((field, i) => {
      const Component = UIFormConfiguration[
        field.type
      ] as FieldComponentFunction<any>;
      return Component(field.properties);
    }),
  );
}

// type FormSet<T extends object> = {
//   Provider: typeof FormProvider<T>;
//   InputLine: <K extends keyof T>() => typeof InputLine<T, K>;
//   InputChoiceHorizontal: <K extends keyof T>() => typeof InputChoiceHorizontal<T, K>;
// };

/**
 * Helper function that created a typed object.
 *
 * @returns
 */
// export function createNewForm<T extends object>() {
//   const res: FormSet<T> = {
//     Provider: FormProvider,
//     InputLine: () => InputLine,
//     InputChoiceHorizontal: () => InputChoiceHorizontal,
//   };
//   return {
//     Provider: res.Provider,
//     InputLine: res.InputLine(),
//     InputChoiceHorizontal: res.InputChoiceHorizontal(),
//   };
// }

/**
 * convert field configuration to render function
 * 
 * @param i18n_ 
 * @param fieldConfig 
 * @param form 
 * @returns 
 */
export function convertUiField(
  i18n_: InternationalizationAPI,
  fieldConfig: UIFormElementConfig[],
  form: object,
  getConverterById: GetConverterById,
): UIFormField[] {
  return fieldConfig.map((config) => {
    // NON input fields
    switch (config.type) {
      case "caption": {
        const resp: UIFormField = {
          type: config.type,
          properties: converBaseFieldsProps(i18n_, config),
        };
        return resp;
      }
      case "group": {
        const resp: UIFormField = {
          type: config.type,
          properties: {
            ...converBaseFieldsProps(i18n_, config),
            fields: convertUiField(i18n_, config.fields, form, getConverterById),
          },
        };
        return resp;
      }
    }
    // Input Fields
    switch (config.type) {
      case "array": {
        return {
          type: "array",
          properties: {
            ...converBaseFieldsProps(i18n_, config),
            ...converInputFieldsProps(form, config, getConverterById),
            labelField: config.labelFieldId,
            fields: convertUiField(i18n_, config.fields, form, getConverterById),
          },
        } as UIFormField;
      }
      case "absoluteTimeText": {
        return {
          type: "absoluteTimeText",
          properties: {
            ...converBaseFieldsProps(i18n_, config),
            ...converInputFieldsProps(form, config, getConverterById),
          },
        } as UIFormField;
      }
      case "amount": {
        return {
          type: "amount",
          properties: {
            ...converBaseFieldsProps(i18n_, config),
            ...converInputFieldsProps(form, config, getConverterById),      
            currency: config.currency,      
          },
        } as UIFormField;
      }
      case "choiceHorizontal": {
        return {
          type: "choiceHorizontal",
          properties: {
            ...converBaseFieldsProps(i18n_, config),
            ...converInputFieldsProps(form, config, getConverterById),
            choices: config.choices,
          },
        } as UIFormField;
      }
      case "choiceStacked": {
        return {
          type: "choiceStacked",
          properties: {
            ...converBaseFieldsProps(i18n_, config),
            ...converInputFieldsProps(form, config, getConverterById),
            choices: config.choices,
            
          },
        }as UIFormField;
      }
      case "file":{
        return {
          type: "file",
          properties: {
            ...converBaseFieldsProps(i18n_, config),
            ...converInputFieldsProps(form, config, getConverterById),
            accept: config.accept,
            maxBites: config.maxBytes,
          },
        } as UIFormField;
      }
      case "integer":{
        return {
          type: "integer",
          properties: {
            ...converBaseFieldsProps(i18n_, config),
            ...converInputFieldsProps(form, config, getConverterById),
          },
        } as UIFormField;
      }
      case "selectMultiple":{
        return {
          type: "selectMultiple",
          properties: {
            ...converBaseFieldsProps(i18n_, config),
            ...converInputFieldsProps(form, config, getConverterById),
            choices: config.choices,
          },
        } as UIFormField;
      }
      case "selectOne": {
        return {
          type: "selectOne",
          properties: {
            ...converBaseFieldsProps(i18n_, config),
            ...converInputFieldsProps(form, config, getConverterById),
            choices: config.choices,
          },
        } as UIFormField;
      }
      case "text": {
        return {
          type: "text",
          properties: {
            ...converBaseFieldsProps(i18n_, config),
            ...converInputFieldsProps(form, config, getConverterById),
          },
        } as UIFormField;
      }
      case "textArea": {
        return {
          type: "text",
          properties: {
            ...converBaseFieldsProps(i18n_, config),
            ...converInputFieldsProps(form, config, getConverterById),
          },
        } as UIFormField;
      }
      case "toggle": {
        return {
          type: "toggle",
          properties: {
            ...converBaseFieldsProps(i18n_, config),
            ...converInputFieldsProps(form, config, getConverterById),
          },
        } as UIFormField;
      }
      default: {
        assertUnreachable(config);
      }
    }
  });
}



function getAddonById(_id: string | undefined): Addon {
  return undefined!;
}


type GetConverterById = (
  id: string | undefined,
  config: unknown,
) => StringConverter<unknown>;


function converInputFieldsProps(
  form: object,
  p: UIFormFieldBaseConfig,
  getConverterById: GetConverterById,
) {
  return {
    converter: getConverterById(p.converterId, p),
    handler: getValueDeeper2(form, p.id.split(".")),
    name: p.name,
    required: p.required,
    disabled: p.disabled,
    help: p.help,
    placeholder: p.placeholder,
    tooltip: p.tooltip,
    label: p.label as TranslatedString,
  };
}

function converBaseFieldsProps(
  i18n_: InternationalizationAPI,
  p: UIFieldElementDescription,
) {
  return {
    after: getAddonById(p.addonAfterId),
    before: getAddonById(p.addonBeforeId),
    hidden: p.hidden,
    name: p.name,
    help: i18n_.str`${p.help}`,
    label: i18n_.str`${p.label}`,
    tooltip: i18n_.str`${p.tooltip}`,
  };
}

export function getValueDeeper2(
  object: Record<string, any>,
  names: string[],
): UIFieldHandler {
  if (names.length === 0) return object as UIFieldHandler;
  const [head, ...rest] = names;
  if (!head) {
    return getValueDeeper2(object, rest);
  }
  if (object === undefined) {
    throw Error("handler not found");
  }
  return getValueDeeper2(object[head], rest);
}


