/*
 This file is part of GNU Taler
 (C) 2025 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/>
 */

import {
  ContactEntry,
  NotificationType,
  Transaction,
  TransactionIdStr,
  TransactionType,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { Fragment, VNode, h } from "preact";
import { useEffect, useState } from "preact/hooks";
import { ErrorAlertView } from "../components/CurrentAlerts.js";
import { Loading } from "../components/Loading.js";
import {
  BoldLight,
  Centered,
  SmallText,
  SmallLightText,
} from "../components/styled/index.js";
import { alertFromError, useAlertContext } from "../context/alert.js";
import { useBackendContext } from "../context/backend.js";
import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
import { Button } from "../mui/Button.js";
import { Grid } from "../mui/Grid.js";
import { Paper } from "../mui/Paper.js";
import { TextField } from "../mui/TextField.js";
import { TextFieldHandler } from "../mui/handlers.js";

interface Props {
  tid?: string;
  onMessageSent?: () => Promise<void>;
}

export function ContactsPage({
  tid,
  onMessageSent
}: Props): VNode {
  const transactionId = tid as TransactionIdStr; //FIXME: validate
  const { i18n } = useTranslationContext();
  const api = useBackendContext();
  const [search, setSearch] = useState<string>();

  const state = useAsyncAsHook(async () => {
    const b = await api.wallet.call(WalletApiOperation.GetContacts, {});
    const contacts = b.contacts;
    const filteredContacts = contacts.filter(c => (!search) ? true : (
                                             c.petname.toLowerCase().includes(search.toLowerCase()) ||
                                             c.alias.toLowerCase().includes(search.toLowerCase()) ||
                                             c.aliasType.toLowerCase().includes(search.toLowerCase()) ||
                                             c.mailboxBaseUri.toLowerCase().includes(search.toLowerCase()) ||
                                             c.source.toLowerCase().includes(search.toLowerCase()))
                                            );
    const tx = tid?
      await api.wallet.call(WalletApiOperation.GetTransactionById, {transactionId})
     : undefined;
    return {
      contacts: filteredContacts,
      transaction: tx
    };
  }, [search, tid]);

  const { pushAlertOnError } = useAlertContext();

  if (!state) {
    return <Loading />;
  }

  useEffect(() => {
    return api.listener.onUpdateNotification(
      [NotificationType.ContactAdded, NotificationType.ContactDeleted],
      state?.retry,
    );
  });
  if (state.hasError) {
    return (
      <ErrorAlertView
        error={alertFromError(
          i18n,
          i18n.str`Could not load the list of contacts`,
          state,
        )}
      />
    );
  }

  const onDeleteContact = async (c: ContactEntry) => {
    api.wallet.call(WalletApiOperation.DeleteContact, { contact: c }).then();
  };
  const onSendUriMessage = async (c: ContactEntry, t?: Transaction) => {
    if (!t) {
      return;
    }
    if ((t.type != TransactionType.PeerPushDebit) &&
        (t.type != TransactionType.PeerPullCredit)) {
      return;
    }
    if (!t.talerUri) {
      return;
    }
    await api.wallet.call(WalletApiOperation.SendTalerUriMailboxMessage, {
      contact: c,
      talerUri:  t.talerUri,
    }).then();
    if (onMessageSent) {
      onMessageSent();
    }
  };
  return (
    <ContactsView
      search={{
        value: search ?? "",
        onInput: pushAlertOnError(async (d: string) => {
          setSearch(d);
        }),
      }}
      contacts={state.response.contacts}
      transaction={state.response.transaction}
      onDeleteContact={onDeleteContact}
      onSendUriMessage={onSendUriMessage}
    />
  );
}

interface ContactProps {
  contact: ContactEntry;
  transaction?: Transaction;
  onDeleteContact: (c: ContactEntry) => Promise<void>;
  onSendUriMessage: (c: ContactEntry, t?: Transaction) => Promise<void>;
}


function ContactLayout(props: ContactProps): VNode {
  const { i18n } = useTranslationContext();
  return (
    <Paper style={{ padding: 8 }}>
      <p>
        <span>{props.contact.petname}</span>
        <SmallText style={{ marginTop: 5 }}>
          <i18n.Translate>Alias</i18n.Translate>: {props.contact.alias}
        </SmallText>
         <SmallText style={{ marginTop: 5 }}>
          <i18n.Translate>Type</i18n.Translate>: {props.contact.aliasType}
        </SmallText>
        <SmallText style={{ marginTop: 5 }}>
          <i18n.Translate>Source</i18n.Translate>: {props.contact.source}
        </SmallText>
         <SmallLightText style={{ marginTop: 5, marginBotton: 5 }}>
          <i18n.Translate>Mailbox</i18n.Translate>: {props.contact.mailboxBaseUri}
        </SmallLightText>
      </p>
      {!props.transaction && (
        <Button variant="contained" onClick={() => { return props.onDeleteContact(props.contact)}} color="error">
          <i18n.Translate>Delete</i18n.Translate>
        </Button>
      )}
      {(props.transaction && (props.transaction.type == TransactionType.PeerPushDebit)) && (
        <Button variant="contained" color="warning" onClick={() => { return props.onSendUriMessage(props.contact, props.transaction)}}>
          <i18n.Translate>Send Cash</i18n.Translate>
        </Button>
      )}
      {(props.transaction && (props.transaction.type == TransactionType.PeerPullCredit)) && (
      <Button variant="contained" color="success" onClick={() => { return props.onSendUriMessage(props.contact, props.transaction)}}>
        <i18n.Translate>Request Cash</i18n.Translate>
      </Button>
      )}
    </Paper>
  );
}

export function ContactsView({
  search,
  contacts,
  transaction,
  onDeleteContact,
  onSendUriMessage: onSendUriMessage,
}: {
  search: TextFieldHandler;
  contacts: ContactEntry[];
  transaction?: Transaction;
  onDeleteContact: (c: ContactEntry) => Promise<void>;
  onSendUriMessage: (c: ContactEntry, t?: Transaction) => Promise<void>;
}): VNode {
  const { i18n } = useTranslationContext();
  return (
    <Fragment>
      <section>
        <TextField
          label="Search"
          variant="filled"
          error={search.error}
          required
          fullWidth
          value={search.value}
          onChange={search.onInput}
        />
        {(contacts.length == 0) ? (
          <Centered style={{ marginTop: 20 }}>
            <BoldLight>
              <i18n.Translate>No contacts found.</i18n.Translate>
            </BoldLight>
          </Centered>
        ) :
        (
        <Grid item container columns={1} spacing={1} style={{ marginTop: 20 }}>
        {
          contacts.map((c, _) => (
            <Grid item xs={1}>
              <ContactLayout
                contact={c}
                transaction={transaction}
                onDeleteContact={onDeleteContact}
                onSendUriMessage={onSendUriMessage}
              />
            </Grid>
          ))
        }
        </Grid>
        )}
       </section>
    </Fragment>
  );
}


