<script setup lang="ts">
import { Icon } from '@iconify/vue';
import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import StatusBadge from './StatusBadge.vue';
import Tabs, { type TabOption } from './Tabs.vue';
import { iconCancel, iconComment, iconDelete, iconReply, iconWarning } from '../icons';
import {
  api,
  getAuthToken,
  moderationUpdateEvent,
  onAuthTokenChange,
  setAuthToken,
  type AiModerationStatus,
  type AuthUser,
  type DiscussionEntityType,
  type EntityDiscussionComment,
  type Language,
  type ModerationUpdateDetail
} from '../services/api';
import Skeleton from './Skeleton.vue';

const props = defineProps<{
  entityType: DiscussionEntityType;
  entityId: string | number;
}>();

const { locale, t } = useI18n();
const comments = ref<EntityDiscussionComment[]>([]);
const languages = ref<Language[]>([]);
const currentUser = ref<AuthUser | null>(null);
const loading = ref(true);
const loadingMore = ref(false);
const authReady = ref(false);
const body = ref('');
const replyBodies = ref<Record<number, string>>({});
const replyTargetId = ref<number | null>(null);
const busyKey = ref('');
const loadError = ref('');
const formError = ref('');
const commentErrors = ref<Record<string, string>>({});
const commentInput = ref<HTMLTextAreaElement | null>(null);
const activeLanguageCode = ref('all');
const moderationBusyId = ref<number | null>(null);
const commentMaxLength = 1000;
const discussionPageSize = 20;
const allLanguageValue = 'all';
let requestId = 0;
let removeAuthListener: (() => void) | null = null;
const nextCursor = ref<string | null>(null);
const hasMoreComments = ref(false);
const commentTotal = ref(0);

function can(permissionKey: string) {
  return currentUser.value?.permissions.includes(permissionKey) === true;
}

const canComment = computed(() => can('discussions.comments.create'));
const charactersLeft = computed(() => Math.max(0, commentMaxLength - body.value.length));
const selectedLanguageCode = computed(() => (activeLanguageCode.value === allLanguageValue ? undefined : activeLanguageCode.value));
const languageTabs = computed<TabOption[]>(() => [
  { value: allLanguageValue, label: t('discussion.allLanguages') },
  ...languages.value.map((language) => ({ value: language.code, label: language.name }))
]);

async function loadCurrentUser() {
  authReady.value = false;

  if (!getAuthToken()) {
    currentUser.value = null;
    authReady.value = true;
    return;
  }

  try {
    const response = await api.me();
    currentUser.value = response.user;
  } catch {
    currentUser.value = null;
    setAuthToken(null);
  } finally {
    authReady.value = true;
  }
}

async function loadLanguages() {
  try {
    languages.value = (await api.languages()).filter((language) => language.enabled);
    if (
      activeLanguageCode.value !== allLanguageValue &&
      !languages.value.some((language) => language.code === activeLanguageCode.value)
    ) {
      activeLanguageCode.value = allLanguageValue;
    }
  } catch {
    languages.value = [];
  }
}

function mergeComments(existing: EntityDiscussionComment[], incoming: EntityDiscussionComment[]) {
  const ids = new Set(existing.map((comment) => comment.id));
  return [...existing, ...incoming.filter((comment) => !ids.has(comment.id))];
}

async function loadDiscussion(reset = true) {
  if (!reset && (loadingMore.value || !hasMoreComments.value)) {
    return;
  }

  const nextRequestId = ++requestId;
  if (reset) {
    loading.value = true;
  } else {
    loadingMore.value = true;
  }
  loadError.value = '';

  try {
    const page = await api.entityDiscussion(props.entityType, props.entityId, {
      limit: discussionPageSize,
      cursor: reset ? null : nextCursor.value,
      language: selectedLanguageCode.value
    });
    if (nextRequestId === requestId) {
      comments.value = reset ? page.items : mergeComments(comments.value, page.items);
      nextCursor.value = page.nextCursor;
      hasMoreComments.value = page.hasMore;
      commentTotal.value = page.total;
    }
  } catch (error) {
    if (nextRequestId === requestId) {
      loadError.value = error instanceof Error && error.message ? error.message : t('errors.loadFailed');
    }
  } finally {
    if (nextRequestId === requestId) {
      loading.value = false;
      loadingMore.value = false;
    }
  }
}

function resetComposer() {
  body.value = '';
  replyBodies.value = {};
  replyTargetId.value = null;
  formError.value = '';
  commentErrors.value = {};
}

function commentKey(commentId: number) {
  return `comment-${commentId}`;
}

function replyBody(commentId: number) {
  return replyBodies.value[commentId] ?? '';
}

function setCommentError(key: string, message: string) {
  commentErrors.value = { ...commentErrors.value, [key]: message };
}

function clearCommentError(key: string) {
  const nextErrors = { ...commentErrors.value };
  delete nextErrors[key];
  commentErrors.value = nextErrors;
}

function canManageComment(comment: EntityDiscussionComment) {
  return (
    !comment.deleted &&
    ((currentUser.value?.id === comment.author?.id && can('discussions.comments.delete')) ||
      can('discussions.comments.delete-any'))
  );
}

function canSeeModeration(comment: EntityDiscussionComment) {
  return currentUser.value?.id === comment.author?.id || can('discussions.comments.delete-any');
}

function canRetryModeration(comment: EntityDiscussionComment) {
  return !comment.deleted && comment.moderationStatus !== 'approved' && comment.moderationStatus !== 'reviewing' && canSeeModeration(comment);
}

function moderationLabel(status: AiModerationStatus) {
  const labels: Record<AiModerationStatus, string> = {
    unreviewed: t('discussion.moderationUnreviewed'),
    reviewing: t('discussion.moderationReviewing'),
    approved: t('discussion.moderationApproved'),
    rejected: t('discussion.moderationRejected'),
    failed: t('discussion.moderationFailed')
  };
  return labels[status];
}

function moderationTone(status: AiModerationStatus) {
  const tones: Record<AiModerationStatus, 'info' | 'success' | 'warning' | 'danger' | 'neutral'> = {
    unreviewed: 'neutral',
    reviewing: 'info',
    approved: 'success',
    rejected: 'danger',
    failed: 'warning'
  };
  return tones[status];
}

function commentAuthorName(comment: EntityDiscussionComment) {
  return comment.deleted ? t('discussion.deletedComment') : comment.author?.displayName ?? t('discussion.byUnknown');
}

function commentInitial(comment: EntityDiscussionComment) {
  return commentAuthorName(comment).slice(0, 1).toUpperCase();
}

function formatDateTime(value: string) {
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) {
    return '';
  }

  return new Intl.DateTimeFormat(locale.value, {
    dateStyle: 'medium',
    timeStyle: 'short'
  }).format(date);
}

function isBusy(key: string) {
  return busyKey.value === key;
}

function startReply(comment: EntityDiscussionComment) {
  replyTargetId.value = comment.id;
  clearCommentError(commentKey(comment.id));
}

function cancelReply(commentId: number) {
  replyTargetId.value = null;
  replyBodies.value[commentId] = '';
  clearCommentError(commentKey(commentId));
}

async function submitComment() {
  const nextBody = body.value.trim();
  if (!nextBody) {
    formError.value = t('discussion.commentRequired');
    commentInput.value?.focus();
    return;
  }

  busyKey.value = 'new-comment';
  formError.value = '';

  try {
    const comment = await api.createEntityDiscussionComment(props.entityType, props.entityId, {
      body: nextBody,
      languageCode: selectedLanguageCode.value ?? null
    });
    comments.value = [...comments.value, comment];
    commentTotal.value += 1;
    body.value = '';
  } catch (error) {
    formError.value = error instanceof Error && error.message ? error.message : t('discussion.commentFailed');
  } finally {
    busyKey.value = '';
  }
}

async function submitReply(comment: EntityDiscussionComment) {
  const key = commentKey(comment.id);
  const nextBody = replyBody(comment.id).trim();
  if (!nextBody) {
    setCommentError(key, t('discussion.commentRequired'));
    return;
  }

  busyKey.value = key;
  clearCommentError(key);

  try {
    const reply = await api.createEntityDiscussionReply(props.entityType, props.entityId, comment.id, {
      body: nextBody,
      languageCode: selectedLanguageCode.value ?? comment.moderationLanguageCode
    });
    comment.replies.push(reply);
    commentTotal.value += 1;
    cancelReply(comment.id);
  } catch (error) {
    setCommentError(key, error instanceof Error && error.message ? error.message : t('discussion.replyFailed'));
  } finally {
    busyKey.value = '';
  }
}

async function retryModeration(comment: EntityDiscussionComment) {
  const key = commentKey(comment.id);
  moderationBusyId.value = comment.id;
  clearCommentError(key);

  try {
    const updated = await api.retryEntityDiscussionModeration(comment.id);
    comment.moderationStatus = updated.moderationStatus;
    comment.moderationLanguageCode = updated.moderationLanguageCode;
  } catch (error) {
    setCommentError(key, error instanceof Error && error.message ? error.message : t('discussion.moderationRetryFailed'));
  } finally {
    moderationBusyId.value = null;
  }
}

function updateDiscussionCommentModeration(
  items: EntityDiscussionComment[],
  commentId: number,
  status: AiModerationStatus,
  languageCode: string | null
): boolean {
  for (const comment of items) {
    if (comment.id === commentId) {
      comment.moderationStatus = status;
      comment.moderationLanguageCode = languageCode;
      return true;
    }

    if (updateDiscussionCommentModeration(comment.replies, commentId, status, languageCode)) {
      return true;
    }
  }

  return false;
}

function isModerationUpdateEvent(event: Event): event is CustomEvent<ModerationUpdateDetail> {
  return event instanceof CustomEvent && event.detail?.type === 'moderation.updated';
}

function handleModerationUpdate(event: Event) {
  if (!isModerationUpdateEvent(event)) {
    return;
  }

  const { target, moderationStatus, moderationLanguageCode } = event.detail;
  if (
    target.type !== 'discussion-comment' ||
    target.discussionCommentId === null ||
    target.entityType !== props.entityType ||
    target.entityId !== Number(props.entityId)
  ) {
    return;
  }

  const updated = updateDiscussionCommentModeration(
    comments.value,
    target.discussionCommentId,
    moderationStatus,
    moderationLanguageCode
  );
  if (updated) {
    comments.value = [...comments.value];
  } else if (moderationStatus === 'approved') {
    void loadDiscussion();
  }
}

function markCommentDeleted(rows: EntityDiscussionComment[], id: number): boolean {
  for (const comment of rows) {
    if (comment.id === id) {
      comment.deleted = true;
      comment.body = '';
      comment.author = null;
      return true;
    }

    if (markCommentDeleted(comment.replies, id)) {
      return true;
    }
  }

  return false;
}

async function deleteComment(comment: EntityDiscussionComment) {
  if (!window.confirm(t('discussion.deleteConfirm'))) {
    return;
  }

  const key = commentKey(comment.id);
  clearCommentError(key);

  try {
    await api.deleteEntityDiscussionComment(comment.id);
    markCommentDeleted(comments.value, comment.id);
    if (replyTargetId.value === comment.id) {
      cancelReply(comment.id);
    }
  } catch (error) {
    setCommentError(key, error instanceof Error && error.message ? error.message : t('discussion.deleteFailed'));
  }
}

watch(
  () => [props.entityType, props.entityId],
  () => {
    resetComposer();
    comments.value = [];
    nextCursor.value = null;
    hasMoreComments.value = false;
    commentTotal.value = 0;
    void loadDiscussion();
  }
);

watch(activeLanguageCode, () => {
  comments.value = [];
  nextCursor.value = null;
  hasMoreComments.value = false;
  commentTotal.value = 0;
  void loadDiscussion();
});

onMounted(() => {
  window.addEventListener(moderationUpdateEvent, handleModerationUpdate);
  void loadCurrentUser();
  void loadLanguages();
  void loadDiscussion();
  removeAuthListener = onAuthTokenChange(() => {
    void loadCurrentUser();
  });
});

onUnmounted(() => {
  window.removeEventListener(moderationUpdateEvent, handleModerationUpdate);
  removeAuthListener?.();
});
</script>

<template>
  <section class="entity-discussion-panel" aria-labelledby="entity-discussion-title">
    <div class="entity-discussion-panel__header">
      <div>
        <h2 id="entity-discussion-title">{{ t('discussion.title') }}</h2>
        <p>{{ t('discussion.count', { count: commentTotal }) }}</p>
      </div>
    </div>

    <Tabs id="entity-discussion-language" v-model="activeLanguageCode" :tabs="languageTabs" :label="t('discussion.languages')" />

    <div v-if="!authReady" class="entity-discussion-skeleton" aria-hidden="true">
      <Skeleton variant="box" height="112px" />
    </div>

    <form v-else-if="canComment" class="entity-discussion-form" @submit.prevent="submitComment">
      <div class="field">
        <label :for="`entity-discussion-comment-${props.entityType}-${props.entityId}`">{{ t('discussion.comment') }}</label>
        <textarea
          :id="`entity-discussion-comment-${props.entityType}-${props.entityId}`"
          ref="commentInput"
          v-model="body"
          :maxlength="commentMaxLength"
          :placeholder="t('discussion.commentPlaceholder')"
        ></textarea>
        <span class="entity-discussion-form__counter">{{ t('discussion.charactersLeft', { count: charactersLeft }) }}</span>
      </div>
      <p v-if="formError" class="entity-discussion-form__error" role="alert">{{ formError }}</p>
      <button class="ui-button ui-button--primary ui-button--small" :disabled="isBusy('new-comment') || !body.trim()" type="submit">
        <Icon :icon="iconComment" class="ui-icon" aria-hidden="true" />
        {{ isBusy('new-comment') ? t('discussion.postingComment') : t('discussion.postComment') }}
      </button>
    </form>

    <div v-else class="entity-discussion-auth-note">
      <p>{{ currentUser ? t('discussion.verifyPrompt') : t('discussion.loginPrompt') }}</p>
      <RouterLink v-if="!currentUser" class="ui-button ui-button--primary ui-button--small" :to="{ path: '/login', query: { redirect: $route.fullPath } }">
        {{ t('nav.login') }}
      </RouterLink>
    </div>

    <div v-if="loading" class="entity-discussion-list" :aria-label="t('discussion.loading')">
      <article v-for="index in 3" :key="index" class="entity-discussion-comment entity-discussion-comment--skeleton">
        <Skeleton variant="box" width="40px" height="40px" />
        <div class="entity-discussion-comment__content">
          <Skeleton width="148px" />
          <Skeleton width="88%" />
          <Skeleton width="62%" />
        </div>
      </article>
    </div>

    <p v-else-if="loadError" class="entity-discussion-form__error" role="alert">{{ loadError }}</p>

    <div v-else-if="comments.length" class="entity-discussion-list">
      <article
        v-for="comment in comments"
        :key="comment.id"
        class="entity-discussion-comment"
        :class="{ 'is-deleted': comment.deleted }"
      >
        <div class="entity-discussion-comment__avatar" aria-hidden="true">{{ commentInitial(comment) }}</div>
        <div class="entity-discussion-comment__content">
          <div class="entity-discussion-comment__meta">
            <RouterLink v-if="!comment.deleted && comment.author" class="user-profile-link" :to="`/profile/${comment.author.id}`">
              {{ comment.author.displayName }}
            </RouterLink>
            <strong v-else>{{ commentAuthorName(comment) }}</strong>
            <time :datetime="comment.createdAt">{{ formatDateTime(comment.createdAt) }}</time>
            <StatusBadge
              v-if="canSeeModeration(comment)"
              :label="moderationLabel(comment.moderationStatus)"
              :tone="moderationTone(comment.moderationStatus)"
              compact
            />
          </div>
          <p v-if="!comment.deleted" class="entity-discussion-comment__body">{{ comment.body }}</p>

          <div v-if="!comment.deleted" class="entity-discussion-comment__actions">
            <button
              v-if="canComment"
              class="life-icon-button life-icon-button--flat"
              type="button"
              :aria-label="t('discussion.reply')"
              @click="startReply(comment)"
            >
              <Icon :icon="iconReply" class="ui-icon" aria-hidden="true" />
              <span class="life-action-tooltip" role="tooltip">{{ t('discussion.reply') }}</span>
            </button>
            <button
              v-if="canRetryModeration(comment)"
              class="life-icon-button life-icon-button--flat"
              type="button"
              :aria-label="t('discussion.moderationRetry')"
              :disabled="moderationBusyId === comment.id"
              @click="retryModeration(comment)"
            >
              <Icon :icon="iconWarning" class="ui-icon" aria-hidden="true" />
              <span class="life-action-tooltip" role="tooltip">
                {{ moderationBusyId === comment.id ? t('discussion.moderationRetrying') : t('discussion.moderationRetry') }}
              </span>
            </button>
            <button
              v-if="canManageComment(comment)"
              class="life-icon-button life-icon-button--flat life-icon-button--danger"
              type="button"
              :aria-label="t('discussion.deleteComment')"
              @click="deleteComment(comment)"
            >
              <Icon :icon="iconDelete" class="ui-icon" aria-hidden="true" />
              <span class="life-action-tooltip" role="tooltip">{{ t('discussion.deleteComment') }}</span>
            </button>
          </div>

          <p v-if="commentErrors[commentKey(comment.id)]" class="entity-discussion-form__error" role="alert">
            {{ commentErrors[commentKey(comment.id)] }}
          </p>

          <form
            v-if="canComment && replyTargetId === comment.id"
            class="entity-discussion-form entity-discussion-form--reply"
            @submit.prevent="submitReply(comment)"
          >
            <div class="field">
              <label :for="`entity-discussion-reply-${comment.id}`">{{ t('discussion.reply') }}</label>
              <textarea
                :id="`entity-discussion-reply-${comment.id}`"
                v-model="replyBodies[comment.id]"
                :maxlength="commentMaxLength"
                :placeholder="t('discussion.replyPlaceholder')"
              ></textarea>
            </div>
            <div class="entity-discussion-form__actions">
              <button
                class="ui-button ui-button--ghost ui-button--small"
                :disabled="isBusy(commentKey(comment.id)) || !replyBody(comment.id).trim()"
                type="submit"
              >
                <Icon :icon="iconReply" class="ui-icon" aria-hidden="true" />
                {{ isBusy(commentKey(comment.id)) ? t('discussion.postingReply') : t('discussion.postReply') }}
              </button>
              <button class="ui-button ui-button--ghost ui-button--small" type="button" @click="cancelReply(comment.id)">
                <Icon :icon="iconCancel" class="ui-icon" aria-hidden="true" />
                {{ t('discussion.cancelReply') }}
              </button>
            </div>
          </form>

          <div v-if="comment.replies.length" class="entity-discussion-replies">
            <article
              v-for="reply in comment.replies"
              :key="reply.id"
              class="entity-discussion-comment entity-discussion-comment--reply"
              :class="{ 'is-deleted': reply.deleted }"
            >
              <div class="entity-discussion-comment__avatar" aria-hidden="true">{{ commentInitial(reply) }}</div>
              <div class="entity-discussion-comment__content">
                <div class="entity-discussion-comment__meta">
                  <RouterLink v-if="!reply.deleted && reply.author" class="user-profile-link" :to="`/profile/${reply.author.id}`">
                    {{ reply.author.displayName }}
                  </RouterLink>
                  <strong v-else>{{ commentAuthorName(reply) }}</strong>
                  <time :datetime="reply.createdAt">{{ formatDateTime(reply.createdAt) }}</time>
                  <StatusBadge
                    v-if="canSeeModeration(reply)"
                    :label="moderationLabel(reply.moderationStatus)"
                    :tone="moderationTone(reply.moderationStatus)"
                    compact
                  />
                </div>
                <p v-if="!reply.deleted" class="entity-discussion-comment__body">{{ reply.body }}</p>
                <div v-if="canManageComment(reply) || canRetryModeration(reply)" class="entity-discussion-comment__actions">
                  <button
                    v-if="canRetryModeration(reply)"
                    class="life-icon-button life-icon-button--flat"
                    type="button"
                    :aria-label="t('discussion.moderationRetry')"
                    :disabled="moderationBusyId === reply.id"
                    @click="retryModeration(reply)"
                  >
                    <Icon :icon="iconWarning" class="ui-icon" aria-hidden="true" />
                    <span class="life-action-tooltip" role="tooltip">
                      {{ moderationBusyId === reply.id ? t('discussion.moderationRetrying') : t('discussion.moderationRetry') }}
                    </span>
                  </button>
                  <button
                    class="life-icon-button life-icon-button--flat life-icon-button--danger"
                    type="button"
                    :aria-label="t('discussion.deleteComment')"
                    @click="deleteComment(reply)"
                  >
                    <Icon :icon="iconDelete" class="ui-icon" aria-hidden="true" />
                    <span class="life-action-tooltip" role="tooltip">{{ t('discussion.deleteComment') }}</span>
                  </button>
                </div>
                <p v-if="commentErrors[commentKey(reply.id)]" class="entity-discussion-form__error" role="alert">
                  {{ commentErrors[commentKey(reply.id)] }}
                </p>
              </div>
            </article>
          </div>
        </div>
      </article>

      <div v-if="hasMoreComments" class="life-feed__retry">
        <button
          class="ui-button ui-button--ghost ui-button--small"
          type="button"
          :disabled="loadingMore"
          @click="loadDiscussion(false)"
        >
          <Icon :icon="iconComment" class="ui-icon" aria-hidden="true" />
          {{ loadingMore ? t('common.loading') : t('discussion.loadMore') }}
        </button>
      </div>
    </div>

    <div v-else class="entity-discussion-empty">
      <Icon :icon="iconComment" class="entity-discussion-empty__icon" aria-hidden="true" />
      <div>
        <h3>{{ t('discussion.empty') }}</h3>
        <p>{{ t('discussion.emptyHint') }}</p>
      </div>
    </div>
  </section>
</template>
