<template>
  <section id="ContactEdit" class="p-6 max-w-3xl mx-auto">
    <TopMessage />

    <!-- Sub View Heading -->
    <div id="SubViewHeading" class="flex gap-4 items-start mb-8">
      <h1 class="grow text-xl text-center font-semibold leading-tight">
        {{ contact?.name || AppString.NO_CONTACT_NAME }}
      </h1>

      <!-- Back -->
      <a
        class="order-first text-lg text-center leading-none p-1"
        @click="$router.go(-1)"
      >
        <font-awesome icon="chevron-left" class="block text-center w-[1em]" />
      </a>

      <!-- Help button -->
      <router-link
        :to="{ name: 'help' }"
        class="block ms-auto text-sm text-center text-white bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] p-1.5 rounded-full"
      >
        <font-awesome icon="question" class="block text-center w-[1em]" />
      </router-link>
    </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">
      <div v-for="(method, index) in contactMethods" :key="index" class="mt-4">
        <!-- Type and Value Row -->
        <div class="flex gap-2">
          <div class="flex-none w-32">
            <label class="block text-xs font-medium text-gray-700 mb-1">
              Type
            </label>
            <select
              v-model="method.type"
              class="block w-full border border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500"
            >
              <option value="">—</option>
              <option
                v-for="methodType in contactMethodTypes"
                :key="methodType.value"
                :value="methodType.value"
              >
                {{ methodType.label }}
              </option>
            </select>
          </div>
          <div class="flex-1">
            <label class="block text-xs font-medium text-gray-700 mb-1">
              Value
            </label>
            <input
              v-model="method.value"
              type="text"
              class="block w-full border border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500"
              placeholder="Number, email, etc."
            />
          </div>
          <button
            class="self-end pb-0.5 text-red-500"
            @click="removeContactMethod(index)"
          >
            <font-awesome icon="trash-can" class="fa-fw" />
          </button>
        </div>

        <!-- WhatsApp Help Text -->
        <div
          v-if="method.type === 'WHATSAPP'"
          class="mt-1 ml-[calc(8rem+0.5rem)] text-xs text-gray-600 italic"
        >
          Must include country code and only numbers (e.g., 12225551234)
        </div>

        <!-- Label Row -->
        <div class="mt-2 flex justify-end">
          <div class="flex-1 ml-[calc(8rem+0.5rem)]">
            <label class="block text-xs font-medium text-gray-700 mb-1">
            </label>
            <input
              v-model="method.label"
              type="text"
              class="block w-full border border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500"
              placeholder="Label / Note"
            />
          </div>
          <div class="w-[2.5rem]"></div>
        </div>
      </div>
      <button class="mt-4" @click="addContactMethod">
        <font-awesome
          icon="plus"
          class="fa-fw px-2 py-2.5 bg-green-500 text-green-100 rounded-full"
        />
      </button>
    </div>

    <!-- Contact Labels -->
    <div class="mt-8 border-t pt-4">
      <label class="block text-sm font-medium text-gray-700"> Labels </label>
      <div class="flex flex-wrap gap-2 mt-2">
        <span
          v-for="label in contactLabels"
          :key="label"
          class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800"
        >
          {{ label }}
          <button
            type="button"
            class="flex-shrink-0 ml-1.5 inline-flex text-blue-400 hover:text-blue-600 focus:outline-none"
            @click="removeLabel(label)"
          >
            <font-awesome icon="xmark" class="h-4 w-4" />
          </button>
        </span>
        <span
          v-if="contactLabels.length === 0"
          class="text-sm text-gray-400 italic"
        >
          No labels attached
        </span>
      </div>
      <div class="mt-2 flex gap-2">
        <input
          v-model="newLabel"
          type="text"
          placeholder="Add a label..."
          class="block w-full border border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 text-sm p-2"
          @keyup.enter="addLabel"
        />
        <button
          class="px-4 py-1 bg-green-500 text-white rounded-md text-sm"
          @click="addLabel"
        >
          Add
        </button>
      </div>
      <!-- All Available Labels -->
      <div v-if="availableLabels.length > 0" class="mt-4">
        <p class="text-sm font-medium text-gray-700 mb-2">Available labels:</p>
        <div class="flex flex-wrap gap-2">
          <button
            v-for="label in availableLabels"
            :key="label"
            class="inline-flex items-center px-3 py-1 rounded text-sm font-medium transition-colors"
            :class="
              contactLabels.includes(label)
                ? 'bg-blue-500 text-white hover:bg-blue-600'
                : 'bg-gray-100 text-gray-800 hover:bg-gray-200'
            "
            @click="toggleLabel(label)"
          >
            {{ label }}
          </button>
        </div>
      </div>
    </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 } from "../constants/app";
import { PlatformServiceMixin } from "../utils/PlatformServiceMixin";
import { createNotifyHelpers, TIMEOUTS } from "../utils/notify";
import {
  NOTIFY_CONTACT_NOT_FOUND,
  NOTIFY_CONTACT_METHODS_UPDATED,
  NOTIFY_CONTACT_SAVED,
} from "../constants/notifications";
import { Contact, ContactMethod } from "../db/tables/contacts";
import { AppString } from "../constants/app";
import { CONTACT_METHOD_TYPES } from "../constants/contacts";

/**
 * 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 database via PlatformServiceMixin
 * 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,
  },
  mixins: [PlatformServiceMixin],
})
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;

  /** Notification helpers */
  notify!: ReturnType<typeof createNotifyHelpers>;

  /** 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> = [];
  /** Array of contact labels */
  contactLabels: string[] = [];
  /** Labels before editing to track changes */
  originalLabels: string[] = [];
  /** New label input field */
  newLabel = "";
  /** All unique labels from other contacts for suggestions */
  allUniqueLabels: string[] = [];

  /** App string constants */
  AppString = AppString;
  /** Contact method types for datalist suggestions */
  contactMethodTypes = CONTACT_METHOD_TYPES;

  /**
   * Filter unique labels that are not already attached to this contact
   */
  get suggestedLabels() {
    return this.allUniqueLabels.filter(
      (label) => !this.contactLabels.includes(label),
    );
  }

  /**
   * Get all available labels for selection
   */
  get availableLabels() {
    return this.allUniqueLabels;
  }

  /**
   * Component lifecycle hook that initializes the contact edit form
   *
   * Workflow:
   * 1. Extracts DID from route parameters
   * 2. Queries database for existing contact via PlatformServiceMixin
   * 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() {
    this.notify = createNotifyHelpers(this.$notify);

    const contactDid = this.$route.params.did as string;
    const contact = await this.$getContact(contactDid);

    if (contact) {
      this.contact = contact;
      this.contactName = contact.name || "";
      this.contactNotes = contact.notes || "";
      this.contactMethods = contact.contactMethods || [];

      // Load labels
      const labels = await this.$getContactLabelsForDid(contactDid);
      this.contactLabels = labels;
      this.originalLabels = [...labels];

      // Load all labels for suggestions
      this.allUniqueLabels = await this.$getUniqueContactLabels();
    } else {
      this.notify.error(
        `${NOTIFY_CONTACT_NOT_FOUND.message} ${contactDid}`,
        TIMEOUTS.LONG,
      );
      (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);
  }

  /**
   * Adds a new label to the contact labels array
   */
  addLabel() {
    const label = this.newLabel.trim();
    if (label && !this.contactLabels.includes(label)) {
      this.contactLabels.push(label);
      this.contactLabels.sort();
      this.newLabel = "";
    }
  }

  /**
   * Adds a suggested label to the contact
   * @param label The label to add
   */
  addSuggestedLabel(label: string) {
    if (!this.contactLabels.includes(label)) {
      this.contactLabels.push(label);
      this.contactLabels.sort();
    }
  }

  /**
   * Removes a label from the contact labels array
   * @param label The label to remove
   */
  removeLabel(label: string) {
    this.contactLabels = this.contactLabels.filter((l) => l !== label);
  }

  /**
   * Toggles a label on or off for the contact
   * @param label The label to toggle
   */
  toggleLabel(label: string) {
    if (this.contactLabels.includes(label)) {
      this.removeLabel(label);
    } else {
      this.contactLabels.push(label);
      this.contactLabels.sort();
    }
  }

  /**
   * 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 via PlatformServiceMixin
   * 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.warning(
        NOTIFY_CONTACT_METHODS_UPDATED.message,
        TIMEOUTS.LONG,
      );
      return;
    }

    // Save to database via PlatformServiceMixin
    // Normalize empty strings to undefined to preserve database consistency
    await this.$updateContact(this.contact?.did || "", {
      name: this.contactName?.trim() || undefined,
      notes: this.contactNotes?.trim() || undefined,
      contactMethods: contactMethods,
    });

    // Save labels
    const contactDid = this.contact?.did || "";
    const labelsToAdd = this.contactLabels.filter(
      (l) => !this.originalLabels.includes(l),
    );
    const labelsToRemove = this.originalLabels.filter(
      (l) => !this.contactLabels.includes(l),
    );

    for (const label of labelsToAdd) {
      await this.$addContactLabel(contactDid, label);
    }
    for (const label of labelsToRemove) {
      await this.$deleteContactLabel(contactDid, label);
    }

    // Notify success and redirect
    this.notify.success(NOTIFY_CONTACT_SAVED.message, TIMEOUTS.STANDARD);
    this.$router.back();
  }
}
</script>
