<template>
  <QuickNav selected="Projects" />
  <TopMessage />

  <section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
    <!-- Heading -->
    <h1 id="ViewHeading" class="text-4xl text-center font-light">Your Ideas</h1>

    <OnboardingDialog ref="onboardingDialog" />

    <!-- Result Tabs -->
    <div class="text-center text-slate-500 border-b border-slate-300 mt-8">
      <ul class="flex flex-wrap justify-center gap-4 -mb-px">
        <li>
          <a
            href="#"
            @click="
              offers = [];
              projects = [];
              showOffers = true;
              showProjects = false;
              loadOffers();
            "
            v-bind:class="computedOfferTabClassNames()"
          >
            Offers
          </a>
        </li>
        <li>
          <a
            href="#"
            @click="
              offers = [];
              projects = [];
              showOffers = false;
              showProjects = true;
              loadProjects();
            "
            v-bind:class="computedProjectTabClassNames()"
          >
            Projects
          </a>
        </li>
      </ul>
    </div>

    <!-- Quick Search -->
    <!--
    <div id="QuickSearch" class="mb-4 flex">
      <input
        type="text"
        placeholder="Search…"
        class="block w-full rounded-l border border-r-0 border-slate-400 px-3 py-2"
      />
      <button
        class="px-4 rounded-r bg-slate-200 border border-l-0 border-slate-400"
      >
        <fa icon="magnifying-glass" class="fa-fw"></fa>
      </button>
    </div>
    -->

    <!-- New Project -->
    <button
      v-if="isRegistered && showProjects"
      class="fixed right-6 top-24 text-center text-4xl leading-none bg-green-600 text-white w-14 py-2.5 rounded-full"
      @click="onClickNewProject()"
    >
      <fa icon="plus" class="fa-fw"></fa>
    </button>

    <!-- Loading Animation -->
    <div
      class="fixed left-6 bottom-24 text-center text-4xl leading-none bg-slate-400 text-white w-14 py-2.5 rounded-full"
      v-if="isLoading"
    >
      <fa icon="spinner" class="fa-spin-pulse"></fa>
    </div>

    <!-- Offer Results List -->
    <InfiniteScroll v-if="showOffers" @reached-bottom="loadMoreOfferData">
      <div v-if="offers.length === 0" class="text-center py-4">
        You have not offered anything.
        <br />
        <router-link to="/discover" class="text-blue-600">
          Look for projects worth some of your time.
        </router-link>
      </div>
      <ul id="listOffers" class="border-t border-slate-300">
        <li
          class="border-b border-slate-300"
          v-for="offer in offers"
          :key="offer.handleId"
        >
          <div class="block py-4 flex gap-4">
            <div v-if="offer.fulfillsPlanHandleId" class="flex-none">
              <ProjectIcon
                :entityId="offer.fulfillsPlanHandleId"
                :iconSize="48"
                class="inline-block align-middle border border-slate-300 rounded-md max-h-12 max-w-12"
              />
            </div>
            <div v-if="offer.recipientDid" class="flex-none w-12">
              <EntityIcon
                :entityId="offer.recipientDid"
                :iconSize="48"
                class="inline-block align-middle border border-slate-300 rounded-md"
              />
            </div>

            <div>
              <div>
                To
                {{
                  offer.fulfillsPlanHandleId
                    ? projectNameFromHandleId[offer.fulfillsPlanHandleId]
                    : didInfo(
                        offer.recipientDid,
                        activeDid,
                        allMyDids,
                        allContacts,
                      )
                }}
              </div>
              <div>
                {{ offer.objectDescription }}
              </div>

              <span class="text-sm">
                <span v-if="offer.amount">
                  <fa
                    :icon="libsUtil.iconForUnitCode(offer.unit)"
                    class="fa-fw text-slate-400"
                  />

                  <span v-if="offer.amountGiven >= offer.amount">
                    <fa icon="check-circle" class="fa-fw text-green-500" />
                    All {{ offer.amount }} given
                  </span>
                  <span v-else>
                    <fa
                      icon="triangle-exclamation"
                      class="fa-fw text-yellow-500"
                    />
                    {{ offer.amountGiven ? "" : "All" }}
                    {{ offer.amount - (offer.amountGiven || 0) }} remaining
                  </span>

                  <span v-if="offer.amountGiven > 0">
                    <span class="text-sm text-slate-400">
                      ({{ offer.amountGiven }} given,
                      <span
                        v-if="offer.amountGivenConfirmed >= offer.amountGiven"
                      >
                        <!--
                         There's no need for a green icon:
                         it's unnecessary if there's already a green, and confusing if there's a yellow.
                        -->
                        all
                      </span>
                      <span v-else>
                        <!-- only show icon if there's not already a warning -->
                        <fa
                          v-if="offer.amountGiven >= offer.amount"
                          icon="triangle-exclamation"
                          class="fa-fw text-yellow-300"
                        />
                        {{ offer.amountGivenConfirmed || 0 }}
                      </span>
                      of that is confirmed)
                    </span>
                  </span>
                </span>
                <span v-else>
                  <!-- Non-amount offer -->
                  <span v-if="offer.nonAmountGivenConfirmed">
                    <fa icon="check-circle" class="fa-fw text-green-500" />
                    {{ offer.nonAmountGivenConfirmed }}
                    {{ offer.nonAmountGivenConfirmed == 1 ? "give" : "gives" }}
                    are confirmed.
                  </span>
                  <span v-else>
                    <fa
                      icon="triangle-exclamation"
                      class="fa-fw text-yellow-500"
                    />
                    <span class="text-sm">Not confirmed by anyone</span>
                  </span>
                </span>

                <a @click="onClickLoadClaim(offer.jwtId)">
                  <fa
                    icon="file-lines"
                    class="pl-2 text-blue-500 cursor-pointer"
                  ></fa>
                </a>
              </span>
            </div>
          </div>
        </li>
      </ul>
    </InfiniteScroll>

    <!-- Project Results List -->
    <InfiniteScroll v-if="showProjects" @reached-bottom="loadMoreProjectData">
      <div v-if="projects.length === 0" class="text-center py-4">
        You have not announced any projects.
        <div v-if="isRegistered">
          Hit the big
          <fa
            icon="plus"
            class="bg-green-600 text-white px-1.5 py-1 rounded-full"
          />
          button. You'll never know until you try.
        </div>
        <div v-else>
          <button
            @click="showNameThenIdDialog()"
            class="text-md font-bold bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mt-2 px-2 py-3 rounded-md"
          >
            Get someone to onboard you.
          </button>
          <UserNameDialog ref="userNameDialog" />
        </div>
      </div>
      <ul id="listProjects" class="border-t border-slate-300">
        <li
          class="border-b border-slate-300"
          v-for="project in projects"
          :key="project.handleId"
        >
          <a
            @click="onClickLoadProject(project.handleId)"
            class="block py-4 flex gap-4"
          >
            <div class="flex-none">
              <ProjectIcon
                :entityId="project.handleId"
                :iconSize="48"
                :imageUrl="project.image"
                class="inline-block align-middle border border-slate-300 rounded-md max-h-12 max-w-12"
              />
            </div>

            <div class="grow overflow-hidden">
              <h2 class="text-base font-semibold">{{ project.name }}</h2>
              <div class="text-sm truncate">
                {{ project.description }}
              </div>
            </div>
          </a>
        </li>
      </ul>
    </InfiniteScroll>
  </section>
</template>

<script lang="ts">
import { AxiosRequestConfig } from "axios";
import { Component, Vue } from "vue-facing-decorator";
import { Router } from "vue-router";

import { NotificationIface } from "@/constants/app";
import { accountsDB, db, retrieveSettingsForActiveAccount } from "@/db/index";
import * as libsUtil from "@/libs/util";
import EntityIcon from "@/components/EntityIcon.vue";
import InfiniteScroll from "@/components/InfiniteScroll.vue";
import QuickNav from "@/components/QuickNav.vue";
import OnboardingDialog from "@/components/OnboardingDialog.vue";
import ProjectIcon from "@/components/ProjectIcon.vue";
import TopMessage from "@/components/TopMessage.vue";
import UserNameDialog from "@/components/UserNameDialog.vue";
import { Contact } from "@/db/tables/contacts";
import {
  didInfo,
  getHeaders,
  getPlanFromCache,
  OfferSummaryRecord,
  PlanData,
} from "@/libs/endorserServer";
import { OnboardPage } from "@/libs/util";

@Component({
  components: {
    EntityIcon,
    InfiniteScroll,
    QuickNav,
    OnboardingDialog,
    ProjectIcon,
    TopMessage,
    UserNameDialog,
  },
})
export default class ProjectsView extends Vue {
  $notify!: (notification: NotificationIface, timeout?: number) => void;
  errNote(message) {
    this.$notify(
      { group: "alert", type: "danger", title: "Error", text: message },
      5000,
    );
  }

  activeDid = "";
  allContacts: Array<Contact> = [];
  allMyDids: Array<string> = [];
  apiServer = "";
  givenName = "";
  isLoading = false;
  isRegistered = false;
  offers: OfferSummaryRecord[] = [];
  projectNameFromHandleId: Record<string, string> = {}; // mapping from handleId to description
  projects: PlanData[] = [];
  showOffers = false;
  showProjects = true;

  libsUtil = libsUtil;
  didInfo = didInfo;

  async mounted() {
    try {
      const settings = await retrieveSettingsForActiveAccount();
      this.activeDid = settings.activeDid || "";
      this.apiServer = settings.apiServer || "";
      this.isRegistered = !!settings.isRegistered;
      this.givenName = settings.firstName || "";

      this.allContacts = await db.contacts.toArray();

      await accountsDB.open();
      const allAccounts = await accountsDB.accounts.toArray();
      this.allMyDids = allAccounts.map((acc) => acc.did);

      if (!settings.finishedOnboarding) {
        (this.$refs.onboardingDialog as OnboardingDialog).open(
          OnboardPage.Create,
        );
      }

      if (allAccounts.length === 0) {
        console.error("No accounts found.");
        this.errNote("You need an identifier to load your projects.");
      } else {
        await this.loadOffers();
      }
    } catch (err) {
      console.error("Error initializing:", err);
      this.errNote("Something went wrong loading your projects.");
    }
  }

  /**
   * Core project data loader
   * @param url the url used to fetch the data
   * @param token Authorization token
   **/
  async projectDataLoader(url: string) {
    try {
      const headers = await getHeaders(this.activeDid);
      this.isLoading = true;
      const resp = await this.axios.get(url, { headers } as AxiosRequestConfig);
      if (resp.status === 200 && resp.data.data) {
        const plans: PlanData[] = resp.data.data;
        for (const plan of plans) {
          const { name, description, handleId, image, issuerDid, rowid } = plan;
          this.projects.push({
            name,
            description,
            image,
            handleId,
            issuerDid,
            rowid,
          });
        }
      } else {
        console.error(
          "Bad server response & data for plans:",
          resp.status,
          resp.data,
        );
        this.errNote("Failed to get projects from the server.");
      }
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
    } catch (error: any) {
      console.error("Got error loading plans:", error.message || error);
      this.errNote("Got an error loading projects.");
    } finally {
      this.isLoading = false;
    }
  }

  /**
   * Data loader used by infinite scroller
   * @param payload is the flag from the InfiniteScroll indicating if it should load
   **/
  async loadMoreProjectData(payload: boolean) {
    if (this.projects.length > 0 && payload) {
      const latestProject = this.projects[this.projects.length - 1];
      await this.loadProjects(`beforeId=${latestProject.rowid}`);
    }
  }

  /**
   * Load projects initially
   * @param issuerDid of the user
   * @param urlExtra additional url parameters in a string
   **/
  async loadProjects(urlExtra: string = "") {
    const url = `${this.apiServer}/api/v2/report/plansByIssuer?${urlExtra}`;
    await this.projectDataLoader(url);
  }

  /**
   * Handle clicking on a project entry found in the list
   * @param id of the project
   **/
  onClickLoadProject(id: string) {
    const route = {
      path: "/project/" + encodeURIComponent(id),
    };
    (this.$router as Router).push(route);
  }

  /**
   * Handling clicking on the new project button
   **/
  onClickNewProject(): void {
    const route = {
      name: "new-edit-project",
    };
    (this.$router as Router).push(route);
  }

  onClickLoadClaim(jwtId: string) {
    const route = {
      path: "/claim/" + encodeURIComponent(jwtId),
    };
    (this.$router as Router).push(route);
  }

  /**
   * Core offer data loader
   * @param url the url used to fetch the data
   * @param token Authorization token
   **/
  async offerDataLoader(url: string) {
    const headers = await getHeaders(this.activeDid);

    try {
      this.isLoading = true;
      const resp = await this.axios.get(url, { headers } as AxiosRequestConfig);
      if (resp.status === 200 && resp.data.data) {
        // add one-by-one as they retrieve project names, potentially from the server
        for (const offer of resp.data.data) {
          if (offer.fulfillsPlanHandleId) {
            const project = await getPlanFromCache(
              offer.fulfillsPlanHandleId,
              this.axios,
              this.apiServer,
              this.activeDid,
            );
            const projectName = project?.name as string;
            this.projectNameFromHandleId[offer.fulfillsPlanHandleId] =
              projectName;
          }
          this.offers = this.offers.concat([offer]);
        }
      } else {
        console.error(
          "Bad server response & data for offers:",
          resp.status,
          resp.data,
        );
        this.$notify(
          {
            group: "alert",
            type: "danger",
            title: "Error",
            text: "Failed to get offers from the server. Try again later.",
          },
          -1,
        );
      }
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
    } catch (error: any) {
      console.error("Got error loading offers:", error.message || error);
      this.$notify(
        {
          group: "alert",
          type: "danger",
          title: "Error",
          text: "Got an error loading offers.",
        },
        -1,
      );
    } finally {
      this.isLoading = false;
    }
  }

  /**
   * Data loader used by infinite scroller
   * @param payload is the flag from the InfiniteScroll indicating if it should load
   **/
  async loadMoreOfferData(payload: boolean) {
    if (this.offers.length > 0 && payload) {
      const latestOffer = this.offers[this.offers.length - 1];
      await this.loadOffers(`&beforeId=${latestOffer.jwtId}`);
    }
  }

  /**
   * Load offers initially
   * @param issuerDid of the user
   * @param urlExtra additional url parameters in a string
   **/
  async loadOffers(urlExtra: string = "") {
    const url = `${this.apiServer}/api/v2/report/offers?offeredByDid=${this.activeDid}${urlExtra}`;
    await this.offerDataLoader(url);
  }

  showNameThenIdDialog() {
    if (!this.givenName) {
      (this.$refs.userNameDialog as UserNameDialog).open(() => {
        this.promptForShareMethod();
      });
    } else {
      this.promptForShareMethod();
    }
  }

  promptForShareMethod() {
    this.$notify(
      {
        group: "modal",
        type: "confirm",
        title: "Are you nearby with cameras?",
        text: "If so, we'll use those with QR codes to share.",
        onCancel: async () => {},
        onNo: async () => {
          (this.$router as Router).push({ name: "share-my-contact-info" });
        },
        onYes: async () => {
          (this.$router as Router).push({ name: "contact-qr" });
        },
        noText: "we will share another way",
        yesText: "we are nearby with cameras",
      },
      -1,
    );
  }

  public computedOfferTabClassNames() {
    return {
      "inline-block": true,
      "py-3": true,
      "rounded-t-lg": true,
      "border-b-2": true,

      active: this.showOffers,
      "text-black": this.showOffers,
      "border-black": this.showOffers,
      "font-semibold": this.showOffers,

      "text-blue-600": !this.showOffers,
      "border-transparent": !this.showOffers,
      "hover:border-slate-400": !this.showOffers,
    };
  }

  public computedProjectTabClassNames() {
    return {
      "inline-block": true,
      "py-3": true,
      "rounded-t-lg": true,
      "border-b-2": true,

      active: this.showProjects,
      "text-black": this.showProjects,
      "border-black": this.showProjects,
      "font-semibold": this.showProjects,

      "text-blue-600": !this.showProjects,
      "border-transparent": !this.showProjects,
      "hover:border-slate-400": !this.showProjects,
    };
  }
}
</script>