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

  <section id="ContactEdit" class="p-6 max-w-3xl mx-auto">
    <div id="ViewBreadcrumb" class="mb-8">
      <h1 class="text-4xl text-center font-light relative px-7">
        <!-- Back -->
        <button
          class="text-lg text-center px-2 py-1 absolute -left-2 -top-1"
          @click="$router.go(-1)"
        >
          <font-awesome icon="chevron-left" class="fa-fw" />
        </button>
        {{ contact.name || AppString.NO_CONTACT_NAME }}
      </h1>
    </div>

    <!-- Contact Name -->
    <div class="mt-4 flex" data-testId="contactName">
      <label
        for="contactName"
        class="block text-sm font-medium text-gray-700 mt-2"
      >
        Name
      </label>
      <input
        v-model="contactName"
        type="text"
        class="block w-full ml-2 mt-1 border border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500"
      />
    </div>

    <!-- Contact Notes -->
    <div class="mt-4">
      <label for="contactNotes" class="block text-sm font-medium text-gray-700">
        Notes
      </label>
      <textarea
        id="contactNotes"
        v-model="contactNotes"
        rows="4"
        class="block w-full mt-1 border border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500"
      ></textarea>
    </div>

    <!-- Contact Methods -->
    <div class="mt-4">
      <h2 class="text-lg font-medium text-gray-700">Contact Methods</h2>
      <div
        v-for="(method, index) in contactMethods"
        :key="index"
        class="flex mt-2"
      >
        <input
          v-model="method.label"
          type="text"
          class="block w-1/4 border border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500"
          placeholder="Label"
        />
        <input
          v-model="method.type"
          type="text"
          class="block ml-2 w-1/4 border border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500"
          placeholder="Type"
        />
        <div class="relative">
          <button
            class="px-2 py-1 bg-gray-200 rounded-md"
            @click="toggleDropdown(index)"
          >
            <font-awesome icon="caret-down" class="fa-fw" />
          </button>
          <div
            v-if="dropdownIndex === index"
            class="absolute bg-white border border-gray-300 rounded-md mt-1"
          >
            <div
              class="px-4 py-2 hover:bg-gray-100 cursor-pointer"
              @click="setMethodType(index, 'CELL')"
            >
              CELL
            </div>
            <div
              class="px-4 py-2 hover:bg-gray-100 cursor-pointer"
              @click="setMethodType(index, 'EMAIL')"
            >
              EMAIL
            </div>
            <div
              class="px-4 py-2 hover:bg-gray-100 cursor-pointer"
              @click="setMethodType(index, 'WHATSAPP')"
            >
              WHATSAPP
            </div>
          </div>
        </div>
        <input
          v-model="method.value"
          type="text"
          class="block ml-2 w-1/2 border border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500"
          placeholder="Number, email, etc."
        />
        <button class="ml-2 text-red-500" @click="removeContactMethod(index)">
          <font-awesome icon="trash-can" class="fa-fw" />
        </button>
      </div>
      <button class="mt-2" @click="addContactMethod">
        <font-awesome
          icon="plus"
          class="fa-fw px-2 py-2.5 bg-green-500 text-green-100 rounded-full"
        />
      </button>
    </div>

    <!-- Save Button -->
    <div class="mt-8 flex justify-between">
      <button
        class="px-4 py-2 bg-blue-500 text-white rounded-md"
        @click="saveEdit"
      >
        Save
      </button>
      <button
        class="ml-4 px-4 py-2 bg-slate-500 text-white rounded-md"
        @click="$router.go(-1)"
      >
        Cancel
      </button>
    </div>
  </section>
</template>

<script lang="ts">
import * as R from "ramda";
import { Component, Vue } from "vue-facing-decorator";
import { RouteLocationNormalizedLoaded, Router } from "vue-router";

import QuickNav from "../components/QuickNav.vue";
import TopMessage from "../components/TopMessage.vue";
import { NotificationIface, USE_DEXIE_DB } from "../constants/app";
import * as databaseUtil from "../db/databaseUtil";
import { parseJsonField } from "../db/databaseUtil";
import { db } from "../db/index";
import { PlatformServiceFactory } from "../services/PlatformServiceFactory";
import { Contact, ContactMethod } from "../db/tables/contacts";
import { AppString } from "../constants/app";

/**
 * Contact Edit View Component
 * @author Matthew Raymer
 *
 * This component provides a full-featured contact editing interface with support for:
 * - Basic contact information (name, notes)
 * - Multiple contact methods with type selection
 * - Data validation and persistence
 *
 * Workflow:
 * 1. Component loads with DID from route params
 * 2. Fetches existing contact data from IndexedDB
 * 3. Presents editable form with current values
 * 4. Validates and saves updates back to database
 *
 * Contact Method Types:
 * - CELL: Mobile phone numbers
 * - EMAIL: Email addresses
 * - WHATSAPP: WhatsApp contact info
 *
 * State Management:
 * - Maintains separate state for form fields to prevent direct mutation
 * - Handles array cloning for contact methods to prevent reference issues
 * - Manages dropdown state for method type selection
 *
 * Navigation:
 * - Back button returns to previous view
 * - Save redirects to contact detail view
 * - Cancel returns to previous view
 * - Invalid DID redirects to contacts list
 */
@Component({
  components: {
    QuickNav,
    TopMessage,
  },
})
export default class ContactEditView extends Vue {
  /** Notification function injected by Vue */
  $notify!: (notification: NotificationIface, timeout?: number) => void;
  /** Current route instance */
  $route!: RouteLocationNormalizedLoaded;
  /** Router instance for navigation */
  $router!: Router;

  /** Current contact data */
  contact: Contact | undefined = {
    did: "",
    name: "",
    notes: "",
  };
  /** Editable contact name field */
  contactName = "";
  /** Editable contact notes field */
  contactNotes = "";
  /** Array of editable contact methods */
  contactMethods: Array<ContactMethod> = [];
  /** Currently open dropdown index, null if none open */
  dropdownIndex: number | null = null;

  /** App string constants */
  AppString = AppString;

  /**
   * Component lifecycle hook that initializes the contact edit form
   *
   * Workflow:
   * 1. Extracts DID from route parameters
   * 2. Queries database for existing contact
   * 3. Populates form fields with contact data
   * 4. Handles missing contact error case
   *
   * @throws Will not throw but redirects on error
   * @emits Notification on contact not found
   * @emits Router navigation on error
   */
  async created() {
    const contactDid = this.$route.params.did;
    const platformService = PlatformServiceFactory.getInstance();
    const dbContact = await platformService.dbQuery(
      "SELECT * FROM contacts WHERE did = ?",
      [contactDid],
    );
    let contact: Contact | undefined = databaseUtil.mapQueryResultToValues(
      dbContact,
    )[0] as unknown as Contact;
    contact.contactMethods = parseJsonField(contact?.contactMethods, []);
    if (USE_DEXIE_DB) {
      await db.open();
      contact = await db.contacts.get(contactDid || "");
    }
    if (contact) {
      this.contact = contact;
      this.contactName = contact.name || "";
      this.contactNotes = contact.notes || "";
      this.contactMethods = contact.contactMethods || [];
    } else {
      this.$notify({
        group: "alert",
        type: "danger",
        title: "Contact Not Found",
        text: "There is no contact with DID " + contactDid,
      });
      (this.$router as Router).push({ path: "/contacts" });
      return;
    }
  }

  /**
   * Adds a new empty contact method to the methods array
   *
   * Creates a new method object with empty fields for:
   * - label: Custom label for the method
   * - type: Communication type (CELL, EMAIL, WHATSAPP)
   * - value: The contact information value
   */
  addContactMethod() {
    this.contactMethods.push({ label: "", type: "", value: "" });
  }

  /**
   * Removes a contact method at the specified index
   *
   * @param index The array index of the method to remove
   */
  removeContactMethod(index: number) {
    this.contactMethods.splice(index, 1);
  }

  /**
   * Toggles the type selection dropdown for a contact method
   *
   * If the clicked dropdown is already open, closes it.
   * If another dropdown is open, closes it and opens the clicked one.
   *
   * @param index The array index of the method whose dropdown to toggle
   */
  toggleDropdown(index: number) {
    this.dropdownIndex = this.dropdownIndex === index ? null : index;
  }

  /**
   * Sets the type for a contact method and closes the dropdown
   *
   * @param index The array index of the method to update
   * @param type The new type value (CELL, EMAIL, WHATSAPP)
   */
  setMethodType(index: number, type: string) {
    this.contactMethods[index].type = type;
    this.dropdownIndex = null;
  }

  /**
   * Saves the edited contact information to the database
   *
   * Workflow:
   * 1. Clones contact methods array to prevent reference issues
   * 2. Normalizes method types to uppercase
   * 3. Checks for changes in method types
   * 4. Updates database with new values
   * 5. Notifies user of success
   * 6. Redirects to contact detail view
   *
   * @throws Will not throw but notifies on validation errors
   * @emits Notification on type changes or success
   * @emits Router navigation on success
   */
  async saveEdit() {
    // without this conversion, "Failed to execute 'put' on 'IDBObjectStore': [object Array] could not be cloned."
    const contactMethodsObj = JSON.parse(JSON.stringify(this.contactMethods));

    // Normalize method types to uppercase
    const contactMethods = contactMethodsObj.map((method: ContactMethod) =>
      R.set(R.lensProp("type"), method.type.toUpperCase(), method),
    );

    // Check for type changes
    if (!R.equals(contactMethodsObj, contactMethods)) {
      this.contactMethods = contactMethods;
      this.$notify(
        {
          group: "alert",
          type: "warning",
          title: "Contact Methods Updated",
          text: "Note that some methods have been updated, such as uppercasing 'email' to 'EMAIL'. Save again if the changes are acceptable.",
        },
        15000,
      );
      return;
    }

    // Save to database
    const platformService = PlatformServiceFactory.getInstance();
    const contactMethodsString = JSON.stringify(contactMethods);
    await platformService.dbExec(
      "UPDATE contacts SET name = ?, notes = ?, contactMethods = ? WHERE did = ?",
      [
        this.contactName,
        this.contactNotes,
        contactMethodsString,
        this.contact?.did || "",
      ],
    );
    if (USE_DEXIE_DB) {
      await db.contacts.update(this.contact?.did || "", {
        name: this.contactName,
        notes: this.contactNotes,
        contactMethods: contactMethods,
      });
    }
    // Notify success and redirect
    this.$notify({
      group: "alert",
      type: "success",
      title: "Contact Saved",
      text: "The contact info has been updated successfully.",
    });
    (this.$router as Router).push({
      path: "/did/" + encodeURIComponent(this.contact?.did || ""),
    });
  }
}
</script>
