<script setup>
import { ref, computed, watch, nextTick } from "vue";
import { useToast } from "primevue/usetoast";

const props = defineProps({
  visible: {
    type: Boolean,
    default: false,
  },
  title: {
    type: String,
    required: true,
  },
  fields: {
    type: Array,
    required: true,
  },
  modelValue: {
    type: Object,
    default: () => ({}),
  },
  loading: {
    type: Boolean,
    default: false,
  },
  validationSchema: {
    type: Object,
    default: null,
  },
  submitLabel: {
    type: String,
    default: "Save",
  },
  cancelLabel: {
    type: String,
    default: "Cancel",
  },
  hideFooter: {
    type: Boolean,
    default: false,
  },
  persistent: {
    type: Boolean,
    default: false,
  },
  submitDisabled: {
    type: Boolean,
    default: false,
  },
  // Field errors returned by the API (422), keyed by field name. Shown on the
  // matching input and cleared as soon as the user edits that field.
  serverErrors: {
    type: Object,
    default: () => ({}),
  },
});

const emit = defineEmits([
  "update:visible",
  "update:modelValue",
  "submit",
  "cancel",
]);

const toast = useToast();
const formRef = ref();
const formData = ref({});
const errors = ref({});
const isValidating = ref(false);
const passwordVisibility = ref({});

// Track which fields are currently shaking
const shakingFields = ref({});

// Trigger the CSS shake animation on a specific field
const triggerShake = (fieldName) => {
  shakingFields.value[fieldName] = true;
  setTimeout(() => {
    shakingFields.value[fieldName] = false;
  }, 400); // matches the 0.4s CSS animation duration
};

// Password strength checker. Every rule here is also enforced by the API, so the
// meter only turns green once the password would actually be accepted — showing
// "Good" for a password the server rejects is what made failures look random.
const PASSWORD_RULES = [
  { label: "8+ characters", test: (v) => v.length >= 8 },
  { label: "an uppercase letter", test: (v) => /[A-Z]/.test(v) },
  { label: "a lowercase letter", test: (v) => /[a-z]/.test(v) },
  { label: "a number", test: (v) => /\d/.test(v) },
  { label: "a symbol", test: (v) => /[^A-Za-z0-9]/.test(v) },
];

const getPasswordStrength = (password) => {
  if (!password) return { strength: 0, text: "", color: "", missing: [] };

  const missing = PASSWORD_RULES.filter((rule) => !rule.test(password)).map(
    (rule) => rule.label,
  );
  const met = PASSWORD_RULES.length - missing.length;

  if (!missing.length)
    return { strength: 4, text: "Strong", color: "#2e7d32", missing };
  if (met >= 4)
    return { strength: 3, text: "Almost there", color: "#ff9800", missing };
  if (met === 3)
    return { strength: 2, text: "Weak", color: "#f44336", missing };
  return { strength: 1, text: "Weak", color: "#f44336", missing };
};

// Initialize form data
const initializeFormData = () => {
  const data = { ...props.modelValue };
  props.fields.forEach((field) => {
    if (!(field.name in data)) {
      data[field.name] = field.defaultValue ?? getDefaultValue(field.type);
    }
    if (field.type === "password") {
      passwordVisibility.value[field.name] = false;
    }
  });
  formData.value = data;
};

// Get default value based on field type
const getDefaultValue = (type) => {
  switch (type) {
    case "checkbox":
    case "switch":
      return false;
    case "multiselect":
    case "autocomplete":
    case "combobox":
      return [];
    case "number":
      return null;
    case "date":
    case "datetime":
    case "time":
      return null;
    default:
      return "";
  }
};

const isVisible = computed({
  get() {
    return props.visible;
  },
  set(value) {
    emit("update:visible", value);
  },
});

// Toggle password visibility
const togglePasswordVisibility = (fieldName) => {
  passwordVisibility.value[fieldName] = !passwordVisibility.value[fieldName];
};

// Validation using Yup
const validateForm = async () => {
  if (!props.validationSchema) return true;

  isValidating.value = true;
  errors.value = {};

  try {
    await props.validationSchema.validate(formData.value, {
      abortEarly: false,
    });
    return true;
  } catch (error) {
    if (error.inner) {
      error.inner.forEach((err) => {
        errors.value[err.path] = err.message;
      });
    }
    return false;
  } finally {
    isValidating.value = false;
  }
};

// Update form data with Real-Time Validation & Shaking
const updateFormData = (fieldName, rawValue, fieldConfig) => {
  let value = rawValue;

  // Intercept and sanitize string inputs
  if (typeof value === "string" && fieldConfig) {
    let originalValue = value;

    // 1. Enforce Alpha-Only (Letters and Spaces)
    if (fieldConfig.alphaOnly) {
      value = value.replace(/[^a-zA-Z\s]/g, "");
    }

    // 2. Enforce Numeric-Only (Numbers and +)
    if (fieldConfig.numericOnly) {
      value = value.replace(/[^0-9+]/g, "");
    }

    // 2.5 Enforce Alphanumeric & Basic Punctuation
    if (fieldConfig.alphanumericOnly) {
      value = value.replace(/[^a-zA-Z0-9\s.,!?'"\-]/g, "");
    }

    // 2.6 Enforce key/slug format: lowercase letters, numbers and underscores only.
    if (fieldConfig.keyFormat) {
      value = value
        .toLowerCase()
        .replace(/\s+/g, "_") // spaces -> underscore
        .replace(/[^a-z0-9_]/g, "") // drop anything else
        .replace(/_{2,}/g, "_"); // collapse repeated underscores
    }

    // 2.7 Strip spam symbols from free-text fields (keeps letters, numbers, spaces,
    // normal punctuation and emojis — blocks @ # $ % ^ & * and similar noise).
    if (fieldConfig.noSymbols) {
      value = value.replace(/[@#$%^&*~`|\\<>{}[\]=+]/g, "");
    }

    // 3. Enforce Max Length
    if (fieldConfig.maxLength && value.length > fieldConfig.maxLength) {
      value = value.slice(0, fieldConfig.maxLength);
    }

    // If the input was modified (i.e., user typed an invalid character or hit the limit), trigger shake
    if (value !== originalValue) {
      triggerShake(fieldName);
    }
  }

  // Handle null/undefined values
  if (value === null || value === undefined) {
    formData.value[fieldName] = null;
  } else {
    formData.value[fieldName] = value;
  }

  emit("update:modelValue", { ...formData.value });

  // Clear error immediately when user types/selects
  if (errors.value[fieldName]) {
    delete errors.value[fieldName];
  }

  // Run validation with a slight delay to ensure value is set
  if (
    props.validationSchema &&
    typeof props.validationSchema.validateAt === "function"
  ) {
    setTimeout(() => {
      props.validationSchema
        .validateAt(fieldName, formData.value)
        .then(() => {
          if (errors.value[fieldName]) delete errors.value[fieldName];
        })
        .catch((err) => {
          errors.value[fieldName] = err.message;
        });
    }, 50);
  }
};

// Form submission
const handleSubmit = async () => {
  const isValid = await validateForm();
  if (isValid) {
    emit("submit", { ...formData.value });
  }
};

const handleCancel = () => {
  emit("cancel");
  emit("update:visible", false);
};

// Get field component props
const getFieldProps = (field) => {
  const baseProps = {
    modelValue: formData.value[field.name],
    label: field.label,
    placeholder: field.placeholder,
    disabled: field.disabled || props.loading,
    readonly: field.readonly,
    required: field.required,
    "error-messages": errors.value[field.name]
      ? [errors.value[field.name]]
      : [],
    color: "#003b6f",
    "base-color": "#003b6f",
    variant: "outlined",
    density: "comfortable",
    "hide-details": !errors.value[field.name] && !field.maxLength && !field.hint, // Show details area if a counter/hint is needed
  };

  // Optional inline hint/help text
  if (field.hint) {
    baseProps.hint = field.hint;
    baseProps["persistent-hint"] = true;
  }

  // Add Vuetify's visual character counter if a maxLength is provided
  if (field.maxLength) {
    baseProps.counter = field.maxLength;
  }

  switch (field.type) {
    case "number":
      return {
        ...baseProps,
        type: "number",
        min: field.min,
        max: field.max,
        step: field.step,
      };
    case "email":
      return {
        ...baseProps,
        type: "email",
      };
    case "password":
      return {
        ...baseProps,
        type: passwordVisibility.value[field.name] ? "text" : "password",
      };
    case "textarea":
      return {
        ...baseProps,
        rows: field.rows || 3,
        "auto-grow": false,
      };
    case "select":
    case "dropdown":
      return {
        ...baseProps,
        items: field.options || [],
        "item-title": field.optionLabel || "title",
        "item-value": field.optionValue || "value",
        clearable: false,
      };
    case "multiselect":
      return {
        ...baseProps,
        items: field.options || [],
        "item-title": field.optionLabel || "title",
        "item-value": field.optionValue || "value",
        multiple: true,
        chips: true,
        "closable-chips": true,
        clearable: true,
      };
    default:
      return baseProps;
  }
};

// Watchers
watch(
  () => props.visible,
  (newValue) => {
    if (newValue) {
      // Delay initialization to ensure modelValue is updated
      setTimeout(() => {
        initializeFormData();
        errors.value = {};
      }, 0);
    }
  },
);

watch(
  () => props.modelValue,
  (newValue) => {
    if (props.visible) {
      formData.value = { ...newValue };
    }
  },
  { deep: true },
);

// Merge API field errors into the same bucket as the Yup ones, so they render on
// the field and get cleared by the existing type-to-clear logic.
watch(
  () => props.serverErrors,
  (newErrors) => {
    Object.entries(newErrors || {}).forEach(([field, message]) => {
      const text = Array.isArray(message) ? message[0] : message;
      if (text) errors.value[field] = text;
    });
  },
  { deep: true, immediate: true },
);

// Initialize on mount
initializeFormData();
</script>

<template>
  <v-navigation-drawer
    v-model="isVisible"
    :temporary="!persistent"
    :persistent="persistent"
    location="right"
    width="450"
    elevation="8"
    class="offcanvas-drawer"
    disable-route-watcher
    @click:outside="handleCancel"
  >
    <v-card class="offcanvas-card" elevation="0">
      <v-card-title
        class="modal-header d-flex align-center justify-space-between px-6 py-4"
      >
        <h3 class="modal-title text-h5 font-weight-bold">
          {{ title }}
        </h3>
        <v-btn
          v-if="!loading"
          icon="bx-x"
          variant="text"
          size="small"
          @click="handleCancel"
          class="close-btn"
        />
      </v-card-title>

      <v-divider />

      <v-card-text class="modal-content pa-6">
        <v-form ref="formRef" @submit.prevent="handleSubmit">
          <div class="form-fields">
            <template v-for="field in fields" :key="field.name">
              <v-text-field
                v-if="
                  field.type === 'text' ||
                  field.type === 'email' ||
                  field.type === 'number'
                "
                v-bind="getFieldProps(field)"
                @update:model-value="updateFormData(field.name, $event, field)"
                :class="[
                  'mb-3 custom-field',
                  { 'shake-animation': shakingFields[field.name] },
                ]"
              />

              <v-text-field
                v-else-if="field.type === 'date'"
                v-bind="getFieldProps(field)"
                type="date"
                @update:model-value="updateFormData(field.name, $event, field)"
                class="mb-3 custom-field"
              />

              <v-text-field
                v-else-if="field.type === 'time'"
                v-bind="getFieldProps(field)"
                type="time"
                @update:model-value="updateFormData(field.name, $event, field)"
                class="mb-3 custom-field"
              />

              <v-text-field
                v-else-if="field.type === 'datetime'"
                v-bind="getFieldProps(field)"
                type="datetime-local"
                @update:model-value="updateFormData(field.name, $event, field)"
                class="mb-3 custom-field"
              />

              <div v-else-if="field.type === 'password'" class="mb-3">
                <v-text-field
                  v-bind="getFieldProps(field)"
                  @update:model-value="
                    updateFormData(field.name, $event, field)
                  "
                  :append-inner-icon="
                    passwordVisibility[field.name] ? 'bx-hide' : 'bx-show'
                  "
                  @click:append-inner="togglePasswordVisibility(field.name)"
                  :class="[
                    'custom-field',
                    { 'shake-animation': shakingFields[field.name] },
                  ]"
                />

                <div
                  v-if="formData[field.name]"
                  class="password-strength-indicator mt-2"
                >
                  <div class="strength-bar">
                    <div
                      class="strength-fill"
                      :style="{
                        width: `${(getPasswordStrength(formData[field.name]).strength / 4) * 100}%`,
                        backgroundColor: getPasswordStrength(
                          formData[field.name],
                        ).color,
                        transition: 'all 0.3s ease',
                      }"
                    ></div>
                  </div>
                  <div
                    class="strength-text"
                    :style="{
                      color: getPasswordStrength(formData[field.name]).color,
                    }"
                  >
                    {{ getPasswordStrength(formData[field.name]).text }}
                  </div>
                </div>

                <div
                  v-if="
                    formData[field.name] &&
                    getPasswordStrength(formData[field.name]).missing.length
                  "
                  class="password-requirements"
                >
                  Still needs:
                  {{
                    getPasswordStrength(formData[field.name]).missing.join(", ")
                  }}
                </div>
              </div>

              <v-textarea
                v-else-if="field.type === 'textarea'"
                v-bind="getFieldProps(field)"
                @update:model-value="updateFormData(field.name, $event, field)"
                :class="[
                  'mb-3 custom-field',
                  { 'shake-animation': shakingFields[field.name] },
                ]"
              />

              <v-select
                v-else-if="field.type === 'select' || field.type === 'dropdown'"
                v-bind="getFieldProps(field)"
                @update:model-value="updateFormData(field.name, $event, field)"
                class="mb-3 custom-field"
              />

              <v-select
                v-else-if="field.type === 'multiselect'"
                v-bind="getFieldProps(field)"
                @update:model-value="updateFormData(field.name, $event, field)"
                class="mb-3 custom-field multiselect-field"
              />

              <v-file-input
                v-else-if="field.type === 'file'"
                v-bind="getFieldProps(field)"
                @update:model-value="updateFormData(field.name, $event, field)"
                :accept="field.accept"
                :multiple="field.multiple"
                class="mb-3 custom-field"
              />

              <v-switch
                v-else-if="field.type === 'switch'"
                v-bind="getFieldProps(field)"
                @update:model-value="updateFormData(field.name, $event, field)"
                class="mb-3"
                inset
              />

              <v-checkbox
                v-else-if="field.type === 'checkbox'"
                v-bind="getFieldProps(field)"
                @update:model-value="updateFormData(field.name, $event, field)"
                class="mb-3"
              />

              <div v-else-if="field.type === 'radio'" class="mb-3">
                <v-label class="field-label text-subtitle-2 mb-2 d-block">
                  {{ field.label }}
                  <span v-if="field.required" class="text-error">*</span>
                </v-label>
                <v-radio-group
                  :model-value="formData[field.name]"
                  @update:model-value="
                    updateFormData(field.name, $event, field)
                  "
                  :inline="false"
                  color="#003b6f"
                  :hide-details="!errors[field.name]"
                  :error-messages="
                    errors[field.name] ? [errors[field.name]] : []
                  "
                >
                  <v-radio
                    v-for="option in field.options"
                    :key="option.value"
                    :label="option.title || option.label"
                    :value="option.value"
                    :disabled="field.disabled || props.loading"
                  />
                </v-radio-group>
              </div>
            </template>
          </div>
          <div v-if="$slots['custom-content']" class="custom-content-section">
            <slot name="custom-content"></slot>
          </div>
        </v-form>
      </v-card-text>

      <template v-if="!hideFooter">
        <v-divider />
        <v-card-actions class="modal-footer px-6 py-4 justify-end">
          <v-btn
            :text="cancelLabel"
            variant="outlined"
            color="grey-darken-1"
            @click="handleCancel"
            :disabled="loading"
            class="me-3"
          />
          <v-btn
            :text="submitLabel"
            color="#003b6f"
            variant="flat"
            @click="handleSubmit"
            :loading="loading || isValidating"
            :disabled="loading || isValidating || submitDisabled"
            class="submit-btn"
          />
        </v-card-actions>
      </template>
    </v-card>
  </v-navigation-drawer>
</template>

<style scoped>
/* ========== REAL-TIME SHAKE ANIMATION ========== */
.shake-animation {
  animation: shake 0.4s cubic-bezier(0.36, 0.07, 0.19, 0.97) both;
}

/* Tint the input background red when shaking for extra visual feedback */
.shake-animation :deep(.v-field) {
  background-color: #fff0f0 !important;
  transition: background-color 0.1s ease;
}

@keyframes shake {
  10%,
  90% {
    transform: translate3d(-1px, 0, 0);
  }
  20%,
  80% {
    transform: translate3d(2px, 0, 0);
  }
  30%,
  50%,
  70% {
    transform: translate3d(-4px, 0, 0);
  }
  40%,
  60% {
    transform: translate3d(4px, 0, 0);
  }
}

/* ========== EXISTING STYLES ========== */
.offcanvas-drawer :deep(.v-navigation-drawer__content) {
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
  padding: 0 !important;
}

.offcanvas-card {
  border-radius: 0 !important;
  box-shadow: none !important;
  height: 100%;
  display: flex;
  flex-direction: column;
  border: none;
}

.modal-header {
  background: linear-gradient(135deg, #003b6f 0%, #004d8f 100%);
  color: white !important;
  border-radius: 0 !important;
  min-height: 80px;
  flex-shrink: 0;
}

.modal-title {
  color: white !important;
  line-height: 1.2;
}

.close-btn {
  color: white !important;
  background: rgba(255, 255, 255, 0.1) !important;
  border-radius: 50% !important;
  width: 32px !important;
  height: 32px !important;
}

.close-btn:hover {
  background: rgba(255, 255, 255, 0.2) !important;
  color: white !important;
  transform: scale(1.1) rotate(90deg);
  transition: all 0.2s ease;
}

.close-btn :deep(.v-icon) {
  color: white !important;
  font-size: 20px !important;
}

.modal-content {
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;
}

.custom-content-section {
  margin-bottom: 1rem;
}

.form-fields {
  display: flex;
  flex-direction: column;
}

.field-label {
  font-weight: 500 !important;
  color: rgba(0, 0, 0, 0.87) !important;
}

/* Remove vertical lines from text fields */
.custom-field :deep(.v-field--variant-filled) {
  background: rgba(0, 59, 111, 0.04) !important;
  border-radius: 8px 8px 0 0 !important;
}

.custom-field :deep(.v-field--variant-filled .v-field__overlay) {
  background: transparent !important;
}

.custom-field :deep(.v-field--variant-filled:before) {
  border-style: none !important;
}

.custom-field :deep(.v-field--variant-filled:after) {
  border-style: solid !important;
  border-width: 0 0 2px 0 !important;
  border-color: #003b6f !important;
  transform: scaleX(0);
  transition: transform 0.3s ease;
}

.custom-field :deep(.v-field--focused.v-field--variant-filled:after) {
  transform: scaleX(1);
}

.password-requirements {
  margin-top: 4px;
  font-size: 12px;
  line-height: 1.4;
  color: rgba(0, 0, 0, 0.6);
}

/* Password strength indicator styles */
.password-strength-indicator {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 8px;
  animation: fadeInUp 0.3s ease;
}

@keyframes fadeInUp {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.strength-bar {
  flex: 1;
  height: 4px;
  background: rgba(0, 0, 0, 0.1);
  border-radius: 2px;
  overflow: hidden;
}

.strength-fill {
  height: 100%;
  border-radius: 2px;
  transition: all 0.3s ease;
  animation: fillBar 0.3s ease;
}

@keyframes fillBar {
  from {
    width: 0% !important;
  }
}

.strength-text {
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  min-width: 60px;
  text-align: right;
  transition: color 0.3s ease;
}

.modal-footer {
  background: rgba(var(--v-theme-surface), 0.9);
  border-top: 1px solid rgba(var(--v-theme-outline), 0.12);
  min-height: 72px;
  flex-shrink: 0;
}

.submit-btn {
  font-weight: 600 !important;
  letter-spacing: 0.5px !important;
  text-transform: none !important;
  border-radius: 6px !important;
  background: linear-gradient(135deg, #003b6f 0%, #004d8f 100%) !important;
  min-width: 100px;
}

.submit-btn:hover {
  box-shadow: 0 4px 12px 0 rgba(0, 59, 111, 0.4) !important;
}

/* MultiSelect specific styles */
.multiselect-field :deep(.v-chip) {
  background: linear-gradient(135deg, #003b6f 0%, #0055a0 100%) !important;
  color: white !important;
  font-weight: 600 !important;
}

.multiselect-field :deep(.v-chip .v-chip__close) {
  color: rgba(255, 255, 255, 0.9) !important;
}

/* Custom field focus styles */
:deep(.v-field--focused .v-field__overlay) {
  opacity: 0.16 !important;
}

:deep(.v-label) {
  color: rgba(0, 0, 0, 0.6) !important;
  font-weight: 400;
}

:deep(.v-field--focused .v-label) {
  color: #003b6f !important;
}

:deep(.v-switch .v-selection-control__wrapper) {
  color: #003b6f;
}

:deep(.v-checkbox .v-selection-control__wrapper) {
  color: #003b6f;
}

:deep(.v-radio .v-selection-control__wrapper) {
  color: #003b6f;
}

/* Mobile responsive */
@media (max-width: 600px) {
  .offcanvas-drawer :deep(.v-navigation-drawer) {
    width: 100% !important;
    max-width: 100%;
  }

  .modal-header {
    padding-left: 16px !important;
    padding-right: 16px !important;
  }

  .modal-content {
    padding-left: 16px !important;
    padding-right: 16px !important;
  }

  .modal-footer {
    padding-left: 16px !important;
    padding-right: 16px !important;
  }

  .password-strength-indicator {
    flex-direction: column;
    align-items: stretch;
    gap: 6px;
  }

  .strength-text {
    text-align: left;
    min-width: auto;
  }
}
</style>
