<template>
  <q-page class="login-page column items-center justify-center">
    <div class="login-overlay" />

    <div class="login-card column items-center">
      <div class="column items-center fields-card">
        <q-img :src="Logo" class="login-logo q-mb-md" />

        <p class="login-title">{{ $t("auth.first_access.form_title") }}</p>
        <p class="login-description">{{ $t("auth.first_access.form_hint") }}</p>

        <div class="flex flex-center q-mb-md">
          <div class="avatar-wrap">
            <q-avatar size="96px" class="first-access-avatar">
              <img v-if="photoPreview" :src="photoPreview" class="avatar-img" />
              <q-icon v-else name="mdi-account" size="52px" color="white" />
            </q-avatar>
            <q-btn
              round
              unelevated
              size="sm"
              icon="mdi-camera-outline"
              class="avatar-edit-btn"
              color="violet-normal"
              text-color="white"
              @click="photoInputRef.click()"
            />
            <input
              ref="photoInputRef"
              type="file"
              accept="image/*"
              capture="user"
              style="display: none"
              @change="onPhotoSelected"
            />
          </div>
        </div>
        <div
          class="text-center text-caption q-mb-md"
          :class="photoError ? 'text-negative' : 'text-grey-7'"
        >
          {{ photoError || $t("auth.first_access.photo_hint") }}
        </div>

        <q-form
          ref="formRef"
          class="full-width"
          autocomplete="off"
          @submit="onSubmit"
        >
          <DefaultInput
            v-model="form.registration"
            v-model:error="validationErrors.registration"
            readonly
            bg-color="grey-2"
            :label="$t('auth.first_access.registration')"
          />

          <DefaultInput
            v-model="form.cpf"
            v-model:error="validationErrors.cpf"
            :readonly="isLocked('cpf')"
            :bg-color="isLocked('cpf') ? 'grey-2' : undefined"
            :mask="masks.Brasil.cpf"
            :label="$t('common.terms.cpf')"
            :rules="[inputRules.required, inputRules.cpf]"
          />

          <DefaultInput
            v-model="form.name"
            v-model:error="validationErrors.name"
            :readonly="isLocked('name')"
            :bg-color="isLocked('name') ? 'grey-2' : undefined"
            :label="$t('common.terms.name')"
            :rules="[inputRules.required, inputRules.minTrimmed(3)]"
          />

          <DefaultInput
            v-model="form.last_name"
            v-model:error="validationErrors.last_name"
            :readonly="isLocked('last_name')"
            :bg-color="isLocked('last_name') ? 'grey-2' : undefined"
            :label="$t('auth.first_access.last_name')"
            :rules="[inputRules.required, inputRules.minTrimmed(1)]"
          />

          <DefaultInput
            v-model="form.email"
            v-model:error="validationErrors.email"
            type="email"
            :readonly="isLocked('email')"
            :bg-color="isLocked('email') ? 'grey-2' : undefined"
            :label="$t('common.terms.email')"
            :rules="[inputRules.required, inputRules.email]"
          />

          <DefaultInput
            v-model="form.phone"
            v-model:error="validationErrors.phone"
            :readonly="isLocked('phone')"
            :bg-color="isLocked('phone') ? 'grey-2' : undefined"
            mask="(##) #####-####"
            :label="$t('auth.first_access.whatsapp')"
            :rules="[inputRules.required]"
          />

          <DefaultSelect
            v-model="form.position_id"
            v-model:error="validationErrors.position_id"
            :options="positionOptions"
            option-value="id"
            option-label="name"
            emit-value
            map-options
            :readonly="isLocked('position_id')"
            :bg-color="isLocked('position_id') ? 'grey-2' : undefined"
            :label="$t('associado.position')"
            :rules="[inputRules.required]"
          />

          <DefaultSelect
            v-model="form.sector_id"
            v-model:error="validationErrors.sector_id"
            :options="sectorOptions"
            option-value="id"
            option-label="name"
            emit-value
            map-options
            :readonly="isLocked('sector_id')"
            :bg-color="isLocked('sector_id') ? 'grey-2' : undefined"
            :label="$t('associado.sector')"
            :rules="[inputRules.required]"
          />

          <DefaultPasswordInput
            v-model="form.password"
            v-model:error="validationErrors.password"
            :label="$t('common.terms.password')"
            :rules="[inputRules.required, inputRules.password]"
          />

          <DefaultPasswordInput
            v-model="form.password_confirmation"
            v-model:error="validationErrors.password_confirmation"
            :label="$t('auth.confirm_password')"
            :rules="[
              inputRules.required,
              inputRules.samePassword(form.password),
            ]"
          />

          <p class="password-hint">{{ $t("auth.password_hint") }}</p>

          <TermsAcceptCheckbox
            v-model="termsAccepted"
            class="q-mt-md"
            :error="termsError"
            @update:model-value="termsError = false"
          />

          <q-btn
            class="full-width q-mt-md login-btn"
            color="violet-normal"
            :label="$t('auth.first_access.finish')"
            type="submit"
            unelevated
            :loading
          >
            <template #loading>
              <q-spinner />
            </template>
          </q-btn>
        </q-form>

        <a href="#" class="forgot-link q-mt-md" @click.prevent="goBack">
          <q-icon name="mdi-arrow-left" size="12px" class="q-mr-xs" />
          {{ $t("auth.first_access.back_to_login") }}
        </a>
      </div>
    </div>
  </q-page>
</template>

<script setup>
import { ref, watch, onBeforeMount, useTemplateRef } from "vue";
import { useQuasar } from "quasar";
import { useI18n } from "vue-i18n";
import { useRouter } from "vue-router";
import { useAuth } from "src/composables/useAuth";
import { useInputRules } from "src/composables/useInputRules";
import { useMediaFile } from "src/composables/useMediaFile";
import { useSubmitHandler } from "src/composables/useSubmitHandler";
import {
  registerFirstAccess,
  getPublicPositions,
  getPublicSectors,
} from "src/api/firstAccess";
import { getFirstAccessData, clearFirstAccessData } from "./firstAccessStorage";
import masks from "src/helpers/masks";

import Logo from "src/assets/logo_serprati.svg";
import DefaultInput from "src/components/defaults/DefaultInput.vue";
import DefaultSelect from "src/components/defaults/DefaultSelect.vue";
import DefaultPasswordInput from "src/components/defaults/DefaultPasswordInput.vue";
import TermsAcceptCheckbox from "src/components/TermsAcceptCheckbox.vue";

const router = useRouter();
const $q = useQuasar();
const { t } = useI18n();
const { inputRules } = useInputRules();
const { login } = useAuth();
const { prepareFile } = useMediaFile();

const formRef = useTemplateRef("formRef");
const photoInputRef = useTemplateRef("photoInputRef");

const token = ref(null);
const lockedFields = ref([]);
const photoFile = ref(null);
const photoPreview = ref(null);
const photoError = ref(null);
const termsAccepted = ref(false);
const termsError = ref(false);
const positionOptions = ref([]);
const sectorOptions = ref([]);

const form = ref({
  registration: null,
  cpf: null,
  name: null,
  last_name: null,
  email: null,
  phone: null,
  position_id: null,
  sector_id: null,
  password: null,
  password_confirmation: null,
});

const isLocked = (field) => lockedFields.value.includes(field);

const unlockField = (field) => {
  lockedFields.value = lockedFields.value.filter((locked) => locked !== field);
};

const notifyUnmappedError = (error) => {
  const response = error?.response;

  if (!response) {
    $q.notify({
      type: "negative",
      timeout: 10000,
      message: t("http.errors.no_connection", {
        detail: error?.message ?? "erro desconhecido",
      }),
    });
    return;
  }

  const unmapped = Object.entries(response.data?.errors ?? {})
    .filter(([field]) => !(field in form.value))
    .map(([field, messages]) => `${field}: ${[messages].flat()[0]}`);

  if (unmapped.length) {
    $q.notify({
      type: "negative",
      timeout: 10000,
      message: t("http.errors.unexpected", { detail: unmapped.join(" | ") }),
    });
    return;
  }

  if (!response.data?.message && response.status !== 422) {
    $q.notify({
      type: "negative",
      timeout: 10000,
      message: t("http.errors.unexpected", {
        detail: `HTTP ${response.status}`,
      }),
    });
  }
};

const {
  loading,
  validationErrors,
  execute: submitForm,
} = useSubmitHandler({ formRef, onError: notifyUnmappedError });

const prefillRules = {
  name: [inputRules.required, inputRules.minTrimmed(3)],
  last_name: [inputRules.required, inputRules.minTrimmed(3)],
  cpf: [inputRules.required, inputRules.cpf],
  email: [inputRules.required, inputRules.email],
  phone: [inputRules.required],
  position_id: [inputRules.required],
  sector_id: [inputRules.required],
};

const unlockInvalidFields = () => {
  Object.entries(prefillRules).forEach(([field, rules]) => {
    if (!isLocked(field)) return;

    if (rules.some((rule) => rule(form.value[field]) !== true)) {
      unlockField(field);
    }
  });
};

watch(
  validationErrors,
  (errors) => Object.keys(errors ?? {}).forEach((field) => unlockField(field)),
  { deep: true },
);

const onPhotoSelected = async (event) => {
  const selected = event.target.files?.[0];
  event.target.value = "";
  if (!selected) return;

  const { file, message } = await prepareFile(selected);

  if (!file) {
    photoError.value = message;
    return;
  }

  photoFile.value = file;
  photoPreview.value = URL.createObjectURL(file);
  photoError.value = null;
};

const onSubmit = async () => {
  const missingPhoto = !photoFile.value && !photoPreview.value;

  if (missingPhoto) {
    photoError.value = t("auth.first_access.photo_required");
  }

  termsError.value = !termsAccepted.value;

  if (missingPhoto || termsError.value) return;

  const password = form.value.password;

  const result = await submitForm(() =>
    registerFirstAccess({
      ...form.value,
      token: token.value,
      photo: photoFile.value,
      terms_accepted: 1,
    }),
  );

  if (!result) return;

  clearFirstAccessData();

  if (!result.can_login) {
    $q.notify({
      type: "warning",
      message: result.message ?? t("auth.first_access.pending_approval"),
      timeout: 8000,
    });
    router.push({ name: "LoginPage" });
    return;
  }

  try {
    await login(form.value.registration, password, { skipSuccessNotify: true });
    $q.notify({ type: "positive", message: t("auth.first_access.success") });
    router.push({ name: "CarteirinhaPage" });
  } catch {
    $q.notify({
      type: "positive",
      message: t("auth.first_access.success_login"),
    });
    router.push({ name: "LoginPage" });
  }
};

const goBack = () => {
  clearFirstAccessData();
  router.push({ name: "LoginPage" });
};

onBeforeMount(async () => {
  const data = getFirstAccessData();

  if (!data?.token) {
    router.replace({ name: "FirstAccessPage" });
    return;
  }

  token.value = data.token;
  form.value.registration = data.registration;

  if (data.user) {
    const { photo_url, ...fields } = data.user;

    Object.entries(fields).forEach(([key, value]) => {
      if (value === null || value === "") return;
      form.value[key] = value;
      lockedFields.value.push(key);
    });

    if (photo_url) {
      photoPreview.value = photo_url;
    }

    unlockInvalidFields();
  }

  try {
    const [positions, sectors] = await Promise.all([
      getPublicPositions(),
      getPublicSectors(),
    ]);
    positionOptions.value = positions ?? [];
    sectorOptions.value = sectors ?? [];
  } catch {
    $q.notify({ type: "negative", message: t("http.errors.failed") });
  }
});
</script>

<style lang="scss" scoped>
@use "sass:map";
@use "src/css/quasar.variables.scss" as *;

.login-page {
  min-height: 100dvh;
  background-image: url("src/assets/pessoas_fundo.jpg");
  background-size: cover;
  background-position: center;
  position: relative;
}

.login-overlay {
  position: absolute;
  inset: 0;
  background: rgba(74, 20, 140, 0.48);
  z-index: 0;
}

.login-card {
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  width: calc(100% - 32px);
  min-height: calc(100dvh - 32px);
  margin: 16px 0;
  background: rgba(255, 255, 255, 0.747);
  border-radius: 16px;
  box-shadow: 0 4px 32px rgba(0, 0, 0, 0.15);
  padding-top: env(safe-area-inset-top);
  padding-bottom: env(safe-area-inset-bottom);
}

.fields-card {
  width: 100%;
  max-width: 400px;
  padding: 32px;
  border-radius: 12px;

  :deep(.q-field__native),
  :deep(.q-field__input) {
    color: $color-text !important;
  }

  :deep(.q-field__control) {
    background: $surface !important;
  }

  :deep(.q-field__label) {
    color: $color-text-2;
  }

  :deep(.q-field--focused .q-field__label) {
    color: $violet-normal;
  }

  :deep(.q-field--float:not(.q-field--focused) .q-field__label) {
    color: $neutral-dark-active;
  }
}

.login-logo {
  width: 220px;
  max-width: 100%;
}

.login-title {
  color: map.get($colors, "violet-normal");
  font-size: 18px;
  font-weight: 700;
  margin: 0 0 8px;
  text-align: center;
}

.login-description {
  color: map.get($colors, "text-2");
  font-size: 13px;
  text-align: center;
  margin: 0 0 16px;
}

.password-hint {
  color: map.get($colors, "text-2");
  font-size: 12px;
  margin: 0;
}

.avatar-wrap {
  position: relative;
  display: inline-block;
}

.first-access-avatar {
  background: rgba(112, 32, 130, 0.18) !important;
}

.avatar-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.avatar-edit-btn {
  position: absolute;
  bottom: -2px;
  right: -2px;
}

.login-btn {
  height: 44px;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 0.5px;
}

.forgot-link {
  color: map.get($colors, "violet-normal");
  font-size: 13px;
  text-decoration: none;
  cursor: pointer;

  &:hover {
    color: map.get($colors, "violet-normal-hover");
    text-decoration: underline;
  }
}
</style>
