/*
 This file is part of GNU Taler
 (C) 2021-2024 Taler Systems S.A.

 GNU Taler is free software; you can redistribute it and/or modify it under the
 terms of the GNU General Public License as published by the Free Software
 Foundation; either version 3, or (at your option) any later version.

 GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

 You should have received a copy of the GNU General Public License along with
 GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */

/**
 *
 * @author Sebastian Javier Marchano (sebasjm)
 */

import {
  AmountString,
  Amounts,
  TalerError,
  TalerMerchantApi,
} from "@gnu-taler/taler-util";
import { Loading, useTranslationContext } from "@gnu-taler/web-util/browser";
import { Fragment, h } from "preact";
import { useCallback, useEffect, useState } from "preact/hooks";
import { useSessionContext } from "../../context/session.js";
import { useInstanceCategories } from "../../hooks/category.js";
import { undefinedIfEmpty } from "../../utils/table.js";
import { ErrorLoadingMerchant } from "../ErrorLoadingMerchant.js";
import { FormProvider } from "../form/FormProvider.js";
import { Input } from "../form/Input.js";
import { InputArray } from "../form/InputArray.js";
import { InputCurrency } from "../form/InputCurrency.js";
import { InputImage } from "../form/InputImage.js";
import { InputNumber } from "../form/InputNumber.js";
import { InputStock, Stock } from "../form/InputStock.js";
import { InputTaxes } from "../form/InputTaxes.js";
import { InputWithAddon } from "../form/InputWithAddon.js";
import { FragmentPersonaFlag } from "../menu/SideBar.js";
import { UIElement } from "../../hooks/preference.js";

const TALER_SCREEN_ID = 23;

type Entity = TalerMerchantApi.ProductDetail & {
  product_id: string;
};

interface Props {
  onSubscribe: (c?: () => Entity | undefined) => void;
  initial?: Partial<Entity>;
  alreadyExist?: boolean;
}

export function ProductForm({ onSubscribe, initial, alreadyExist }: Props) {
  const { i18n } = useTranslationContext();
  const { state, lib } = useSessionContext();
  // const [preference] = usePreference();

  // FIXME: if the category list is big the will bring a lot of info
  // we could find a lazy way to add up on searches
  const categoriesResult = useInstanceCategories();
  if (!categoriesResult) return <Loading />;
  if (categoriesResult instanceof TalerError) {
    return <ErrorLoadingMerchant error={categoriesResult} />;
  }
  const categories =
    categoriesResult.type === "fail" ? [] : categoriesResult.body.categories;
  const [value, valueHandler] = useState<
    Partial<
      Entity & {
        stock: Stock;
        categories_map: { id: string; description: string }[];
      }
    >
  >({
    address: {},
    description_i18n: {},
    taxes: [],
    categories: [],
    next_restock: { t_s: "never" },
    price: ":0" as AmountString,
    ...initial,
    minimum_age: !initial?.minimum_age ? undefined : initial?.minimum_age,
    stock:
      !initial || initial.total_stock === -1
        ? undefined
        : {
            current: initial.total_stock || 0,
            lost: initial.total_lost || 0,
            sold: initial.total_sold || 0,
            address: initial.address,
            nextRestock: initial.next_restock,
          },
  });

  useEffect(() => {
    if (!initial || !initial?.categories) return;

    const ps = initial.categories.map((catId) => {
      return lib.instance
        .getCategoryDetails(state.token, String(catId))
        .then((res) => {
          return res.type === "fail"
            ? undefined
            : { id: String(catId), description: res.body.name };
        });
    });
    Promise.all(ps).then((all) => {
      const categories_map = all.filter(notEmpty);
      valueHandler({ ...value, categories_map });
    });
  }, []);

  const errors = undefinedIfEmpty({
    product_id: !value.product_id ? i18n.str`Required` : undefined,
    product_name: !value.product_name ? i18n.str`Required` : undefined,
    description: !value.description ? i18n.str`Required` : undefined,
    unit: !value.unit ? i18n.str`Required` : undefined,
    price: !value.price
      ? i18n.str`Required`
      : Amounts.parse(value.price) === undefined
        ? i18n.str`Invalid amount`
        : undefined,
    minimum_age:
      value.minimum_age === undefined
        ? undefined
        : value.minimum_age < 1
          ? i18n.str`Must be greater than 0`
          : undefined,
  });

  if (alreadyExist && errors) {
    // on update, we remove some validations
    delete errors.product_id;
  }

  const hasErrors = errors !== undefined;

  const submit = useCallback((): Entity | undefined => {
    const stock = value.stock;

    if (!stock) {
      // the the input was untouched, then set the same
      // initial value
      value.total_stock = initial?.total_stock;
      value.total_lost = initial?.total_lost;
      value.next_restock = initial?.next_restock;
      value.address = initial?.address;

      if (!value.total_stock) {
        value.total_stock = -1;
      }
    } else {
      value.total_stock = stock.current;
      value.total_lost = stock.lost;
      value.next_restock =
        stock.nextRestock instanceof Date
          ? { t_s: stock.nextRestock.getTime() / 1000 }
          : stock.nextRestock;
      value.address = stock.address;
    }
    delete value.stock;
    value.categories = value.categories_map?.map((d) => parseInt(d.id, 10));
    delete value.categories_map;

    if (typeof value.minimum_age !== "undefined" && value.minimum_age < 1) {
      delete value.minimum_age;
    }

    return value as TalerMerchantApi.ProductDetail & {
      product_id: string;
    };
  }, [value]);

  useEffect(() => {
    onSubscribe(hasErrors ? undefined : submit);
  }, [submit, hasErrors]);

  return (
    <div>
      <FormProvider<Entity>
        name="product"
        errors={errors}
        object={value}
        valueHandler={valueHandler}
      >
        {/**
         * If the user press enter on any text field it will the browser will trigger
         * the first button that it found.
         *
         * In this form the InputImage will be triggered and this is unwanted.
         *
         * As a workaround we have this non-action button which will prevent loading/unloading
         * the image when the enter key is pressed accidentally.
         */}
        <button style={{ visibility: "hidden" }} />
        {alreadyExist ? undefined : (
          <InputWithAddon<Entity>
            name="product_id"
            // addonBefore={new URL("product/", state.backendUrl.href).href}
            label={i18n.str`ID`}
            addonBeforeLarge={state.backendUrl.href.length > 50}
            expand
            tooltip={i18n.str`Product identification to use in URLs (for internal use only).`}
          />
        )}
        <InputImage<Entity>
          name="image"
          label={i18n.str`Image`}
          tooltip={i18n.str`Illustration of the product for customers.`}
        />
        <Input<Entity>
          name="product_name"
          label={i18n.str`Name`}
          tooltip={i18n.str`Product name.`}
        />
        <Input<Entity>
          name="description"
          inputType="multiline"
          label={i18n.str`Description`}
          tooltip={i18n.str`Product description for customers.`}
        />
        <FragmentPersonaFlag point={UIElement.option_ageRestriction}>
          <InputNumber<Entity>
            name="minimum_age"
            label={i18n.str`Age restriction`}
            tooltip={i18n.str`Is this product restricted for customer below certain age?`}
            help={i18n.str`Minimum age of the customer`}
          />
        </FragmentPersonaFlag>
        <Input<Entity>
          name="unit"
          label={i18n.str`Unit name`}
          tooltip={i18n.str`Unit describing quantity of product sold (e.g. 2 kilograms, 5 liters, 3 items, 5 meters) for customers.`}
          help={i18n.str`Example: kg, items or liters`}
        />
        <InputCurrency<Entity>
          name="price"
          label={i18n.str`Price per unit`}
          tooltip={i18n.str`Sale price for customers, including taxes, for above units of the product.`}
        />
        <InputStock
          name="stock"
          label={i18n.str`Stock`}
          alreadyExist={alreadyExist}
          tooltip={i18n.str`Inventory for products with finite supply (for internal use only).`}
        />
        <InputTaxes<Entity>
          name="taxes"
          label={i18n.str`Taxes`}
          tooltip={i18n.str`Taxes included in the product price, exposed to customers.`}
        />
        <InputArray
          name="categories_map"
          label={i18n.str`Categories`}
          getSuggestion={async () => {
            return categories.map((cat) => {
              return { description: cat.name, id: String(cat.category_id) };
            });
          }}
          help={i18n.str`Search by category description or id`}
          tooltip={i18n.str`Categories where this product will be listed on.`}
          unique
        />
      </FormProvider>
    </div>
  );
}

function notEmpty<TValue>(value: TValue | null | undefined): value is TValue {
  return value !== null && value !== undefined;
}
