<template>
  <div v-if="visible" class="dialog-overlay">
    <div class="dialog">
      <div class="text-slate-900 text-center">
        <h3 class="text-lg font-semibold leading-[1.25] mb-2">
          {{ title }}
        </h3>
        <p class="text-sm mb-4">
          {{ description }}
        </p>

        <!-- Member Selection Table -->
        <div class="mb-4">
          <table
            class="w-full border-collapse border border-slate-300 text-sm text-start"
          >
            <!-- Select All Header -->
            <thead v-if="membersData && membersData.length > 0">
              <tr class="bg-slate-100 font-medium">
                <th class="border border-slate-300 px-3 py-2">
                  <label class="flex items-center gap-2">
                    <input
                      type="checkbox"
                      :checked="isAllSelected"
                      :indeterminate="isIndeterminate"
                      @change="toggleSelectAll"
                    />
                    Select All
                  </label>
                </th>
              </tr>
            </thead>
            <tbody>
              <!-- Empty State -->
              <tr v-if="!membersData || membersData.length === 0">
                <td
                  class="border border-slate-300 px-3 py-2 text-center italic text-gray-500"
                >
                  {{ emptyStateText }}
                </td>
              </tr>
              <!-- Member Rows -->
              <tr
                v-for="member in membersData || []"
                :key="member.member.memberId"
              >
                <td class="border border-slate-300 px-3 py-2">
                  <div class="flex items-center justify-between gap-2">
                    <label class="flex items-center gap-2">
                      <input
                        type="checkbox"
                        :checked="isMemberSelected(member.did)"
                        @change="toggleMemberSelection(member.did)"
                      />
                      <div class="">
                        <div class="text-sm font-semibold">
                          {{ member.name || SOMEONE_UNNAMED }}
                        </div>
                        <div
                          class="flex items-center gap-0.5 text-xs text-slate-500"
                        >
                          <span class="font-semibold sm:hidden">DID:</span>
                          <span
                            class="w-[35vw] sm:w-auto truncate text-left"
                            style="direction: rtl"
                            >{{ member.did }}</span
                          >
                        </div>
                      </div>
                    </label>

                    <!-- Contact indicator - only show if they are already a contact -->
                    <font-awesome
                      v-if="member.isContact"
                      icon="user-circle"
                      class="fa-fw ms-auto text-slate-400 cursor-pointer hover:text-slate-600"
                      @click="showContactInfo"
                    />
                  </div>
                </td>
              </tr>
            </tbody>
            <!-- Select All Footer -->
            <tfoot v-if="membersData && membersData.length > 0">
              <tr class="bg-slate-100 font-medium">
                <th class="border border-slate-300 px-3 py-2">
                  <label class="flex items-center gap-2">
                    <input
                      type="checkbox"
                      :checked="isAllSelected"
                      :indeterminate="isIndeterminate"
                      @change="toggleSelectAll"
                    />
                    Select All
                  </label>
                </th>
              </tr>
            </tfoot>
          </table>
        </div>

        <!-- Action Buttons -->
        <div class="space-y-2">
          <!-- Main Action Button -->
          <button
            v-if="membersData && membersData.length > 0"
            :disabled="!hasSelectedMembers"
            :class="[
              'block w-full text-center text-md font-bold uppercase px-2 py-2 rounded-md',
              hasSelectedMembers
                ? 'bg-blue-600 text-white cursor-pointer'
                : 'bg-slate-400 text-slate-200 cursor-not-allowed',
            ]"
            @click="handleMainAction"
          >
            {{ buttonText }}
          </button>
          <!-- Cancel Button -->
          <button
            class="block w-full text-center text-md font-bold uppercase bg-slate-600 text-white px-2 py-2 rounded-md"
            @click="cancel"
          >
            Maybe Later
          </button>
        </div>
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import { Vue, Component, Prop } from "vue-facing-decorator";

import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { SOMEONE_UNNAMED } from "@/constants/entities";
import { MemberData } from "@/interfaces";
import { setVisibilityUtil, getHeaders, register } from "@/libs/endorserServer";
import { createNotifyHelpers } from "@/utils/notify";
import { Contact } from "@/db/tables/contacts";

@Component({
  mixins: [PlatformServiceMixin],
  emits: ["close"],
})
export default class BulkMembersDialog extends Vue {
  @Prop({ default: "" }) activeDid!: string;
  @Prop({ default: "" }) apiServer!: string;
  // isOrganizer: true = organizer mode (admit members), false = member mode (set visibility)
  @Prop({ required: true }) isOrganizer!: boolean;

  // Vue notification system
  $notify!: (
    notification: { group: string; type: string; title: string; text: string },
    timeout?: number,
  ) => void;

  // Notification system
  notify!: ReturnType<typeof createNotifyHelpers>;

  // Component state
  membersData: MemberData[] = [];
  selectedMembers: string[] = [];
  visible = false;

  // Constants
  // In Vue templates, imported constants need to be explicitly made available to the template
  readonly SOMEONE_UNNAMED = SOMEONE_UNNAMED;

  get hasSelectedMembers() {
    return this.selectedMembers.length > 0;
  }

  get isAllSelected() {
    if (!this.membersData || this.membersData.length === 0) return false;
    return this.membersData.every((member) =>
      this.selectedMembers.includes(member.did),
    );
  }

  get isIndeterminate() {
    if (!this.membersData || this.membersData.length === 0) return false;
    const selectedCount = this.membersData.filter((member) =>
      this.selectedMembers.includes(member.did),
    ).length;
    return selectedCount > 0 && selectedCount < this.membersData.length;
  }

  get title() {
    return this.isOrganizer
      ? "Admit Pending Members"
      : "Add Members to Contacts";
  }

  get description() {
    return this.isOrganizer
      ? "Would you like to admit these members to the meeting and add them to your contacts?"
      : "Would you like to add these members to your contacts?";
  }

  get buttonText() {
    return this.isOrganizer ? "Admit + Add to Contacts" : "Add to Contacts";
  }

  get emptyStateText() {
    return this.isOrganizer
      ? "No pending members to admit"
      : "No members are not in your contacts";
  }

  created() {
    this.notify = createNotifyHelpers(this.$notify);
  }

  open(members: MemberData[]) {
    this.visible = true;
    this.membersData = members;
    // Select all by default
    this.selectedMembers = this.membersData.map((member) => member.did);
  }

  close(notSelectedMemberDids: string[]) {
    this.visible = false;
    this.$emit("close", { notSelectedMemberDids: notSelectedMemberDids });
  }

  cancel() {
    this.close(this.membersData.map((member) => member.did));
  }

  toggleSelectAll() {
    if (!this.membersData || this.membersData.length === 0) return;

    if (this.isAllSelected) {
      // Deselect all
      this.selectedMembers = [];
    } else {
      // Select all
      this.selectedMembers = this.membersData.map((member) => member.did);
    }
  }

  toggleMemberSelection(memberDid: string) {
    const index = this.selectedMembers.indexOf(memberDid);
    if (index > -1) {
      this.selectedMembers.splice(index, 1);
    } else {
      this.selectedMembers.push(memberDid);
    }
  }

  isMemberSelected(memberDid: string) {
    return this.selectedMembers.includes(memberDid);
  }

  async handleMainAction() {
    // isOrganizer: true = admit mode, false = visibility mode
    if (this.isOrganizer) {
      await this.organizerAdmitAndAddWithVisibility();
    } else {
      await this.memberAddContactWithVisibility();
    }
  }

  async organizerAdmitAndAddWithVisibility() {
    try {
      const selectedMembers: MemberData[] = this.membersData.filter((member) =>
        this.selectedMembers.includes(member.did),
      );
      const notSelectedMembers: MemberData[] = this.membersData.filter(
        (member) => !this.selectedMembers.includes(member.did),
      );

      let admittedCount = 0;
      let contactAddedCount = 0;
      let errors = 0;

      for (const member of selectedMembers) {
        try {
          // First, admit the member
          await this.admitMember(member);

          // Register them
          await this.registerMember(member);
          admittedCount++;

          // If they're not a contact yet, add them as a contact
          if (!member.isContact) {
            await this.addAsContact(member, true);
            contactAddedCount++;
          }

          // Set their seesMe to true
          await this.updateContactVisibility(member.did, true);
        } catch (error) {
          // eslint-disable-next-line no-console
          console.error(`Error processing member ${member.did}:`, error);
          // Continue with other members even if one fails
          errors++;
        }
      }

      // Show success notification
      if (admittedCount > 0) {
        this.$notify(
          {
            group: "alert",
            type: "success",
            title: "Members Admitted Successfully",
            text: `${admittedCount} member${admittedCount === 1 ? "" : "s"} admitted and registered${contactAddedCount === 0 ? "" : admittedCount === contactAddedCount ? " and" : `, ${contactAddedCount}`}${contactAddedCount === 0 ? "" : ` added as contact${contactAddedCount === 1 ? "" : "s"}`}.`,
          },
          10000,
        );
      }
      if (errors > 0) {
        this.$notify(
          {
            group: "alert",
            type: "danger",
            title: "Error",
            text: "Failed to fully admit some members. Work with them individually below.",
          },
          5000,
        );
      }

      this.close(notSelectedMembers.map((member) => member.did));
    } catch (error) {
      // eslint-disable-next-line no-console
      console.error("Error admitting members:", error);
      this.$notify(
        {
          group: "alert",
          type: "danger",
          title: "Error",
          text: "Some errors occurred. Work with members individually below.",
        },
        5000,
      );
    }
  }

  async memberAddContactWithVisibility() {
    try {
      const selectedMembers: MemberData[] = this.membersData.filter((member) =>
        this.selectedMembers.includes(member.did),
      );
      const notSelectedMembers: MemberData[] = this.membersData.filter(
        (member) => !this.selectedMembers.includes(member.did),
      );

      let contactsAddedCount = 0;

      for (const member of selectedMembers) {
        try {
          // If they're not a contact yet, add them as a contact first
          if (!member.isContact) {
            await this.addAsContact(member, undefined);
            contactsAddedCount++;
          }

          // Set their seesMe to true
          await this.updateContactVisibility(member.did, true);
        } catch (error) {
          // eslint-disable-next-line no-console
          console.error(`Error processing member ${member.did}:`, error);
          // Continue with other members even if one fails
        }
      }

      // Show success notification
      this.$notify(
        {
          group: "alert",
          type: "success",
          title: "Contacts Added Successfully",
          text: `${contactsAddedCount} member${contactsAddedCount === 1 ? "" : "s"} added as contact${contactsAddedCount === 1 ? "" : "s"}.`,
        },
        5000,
      );

      this.close(notSelectedMembers.map((member) => member.did));
    } catch (error) {
      // eslint-disable-next-line no-console
      console.error("Error adding contacts:", error);
      this.$notify(
        {
          group: "alert",
          type: "danger",
          title: "Error",
          text: "Some errors occurred. Work with members individually below.",
        },
        5000,
      );
    }
  }

  async admitMember(member: {
    did: string;
    name: string;
    member: { memberId: string };
  }) {
    try {
      const headers = await getHeaders(this.activeDid);
      await this.axios.put(
        `${this.apiServer}/api/partner/groupOnboardMember/${member.member.memberId}`,
        { admitted: true },
        { headers },
      );
    } catch (err) {
      // eslint-disable-next-line no-console
      console.error("Error admitting member:", err);
      throw err;
    }
  }

  async registerMember(member: MemberData) {
    try {
      const contact: Contact = { did: member.did };
      const result = await register(
        this.activeDid,
        this.apiServer,
        this.axios,
        contact,
      );
      if (result.success) {
        if (result.embeddedRecordError) {
          throw new Error(result.embeddedRecordError);
        }
        await this.$updateContact(member.did, { registered: true });
      } else {
        throw result;
      }
    } catch (err) {
      // eslint-disable-next-line no-console
      console.error("Error registering member:", err);
      throw err;
    }
  }

  async addAsContact(
    member: { did: string; name: string },
    isRegistered?: boolean,
  ) {
    try {
      const newContact: Contact = {
        did: member.did,
        name: member.name,
        registered: isRegistered,
      };

      await this.$insertContact(newContact);
    } catch (err) {
      // eslint-disable-next-line no-console
      console.error("Error adding contact:", err);
      if (err instanceof Error && err.message?.indexOf("already exists") > -1) {
        // Contact already exists, continue
      } else {
        throw err; // Re-throw if it's not a duplicate error
      }
    }
  }

  async updateContactVisibility(did: string, seesMe: boolean) {
    try {
      // Get the contact object
      const contact = await this.$getContact(did);
      if (!contact) {
        throw new Error(`Contact not found for DID: ${did}`);
      }

      // Use the proper API to set visibility on the server
      const result = await setVisibilityUtil(
        this.activeDid,
        this.apiServer,
        this.axios,
        contact,
        seesMe,
      );

      if (!result.success) {
        throw new Error(result.error || "Failed to set visibility");
      }
    } catch (err) {
      // eslint-disable-next-line no-console
      console.error("Error updating contact visibility:", err);
      throw err;
    }
  }

  showContactInfo() {
    // isOrganizer: true = admit mode, false = visibility mode
    const message = this.isOrganizer
      ? "This user is already your contact, but they are not yet admitted to the meeting."
      : "This user is already your contact, but your activities are not visible to them yet.";

    this.$notify(
      {
        group: "alert",
        type: "info",
        title: "Contact Info",
        text: message,
      },
      5000,
    );
  }
}
</script>
