// src/client/entities/access.ts
// Access entity - Pinia store for external access management

import { defineStore } from 'pinia';
import { ref, computed } from 'vue';
import { accessRepository, type AccessPendingEntryResponse } from '../shared/api/repositories/AccessRepository';
import type {
  ExternalAccess,
  ExternalAccessUnifiedEntry,
  CreateAccessRequest,
  UpdateAccessRequest,
} from '@shared/contracts/access';
import { EVENTS } from '@shared/contracts/events';
import { socketService } from '../shared/api/SocketService';

// Re-export types for backward compatibility
export type Access = ExternalAccess;
export type CreateAccessInput = CreateAccessRequest;
export type UpdateAccessInput = UpdateAccessRequest;

export const useAccessStore = defineStore('access', () => {
  const entries = ref<Access[]>([]);
  const pendingEntries = ref<AccessPendingEntryResponse[]>([]);
  const loading = ref(false);
  const error = ref<string | null>(null);
  const searchQuery = ref<string>('');
  const socketBound = ref(false);
  const socketUnsubscribers = ref<(() => void)[]>([]);

  const items = computed(() => entries.value);
  const hasPendingEntries = computed(() => pendingEntries.value.length > 0);

  const unifiedList = computed<ExternalAccessUnifiedEntry[]>(() => {
    const targetIds = new Set(
      pendingEntries.value
        .filter((p) => p.targetId !== null)
        .map((p) => p.targetId as string),
    );

    const approvedEntries = entries.value
      .filter((e) => !targetIds.has(e.id))
      .map((e) => ({ ...e } as ExternalAccessUnifiedEntry));

    const pendingAsUnified = pendingEntries.value.map((p) => {
      const original = p.targetId ? entries.value.find((e) => e.id === p.targetId) : null;

      return {
        id: p.id,
        resourceName: p.resourceName,
        link: p.link,
        login: p.login,
        password: p.password,
        comment: p.comment,
        isApproved: false,
        createdBy: p.createdBy,
        createdAt: p.createdAt,
        updatedAt: p.updatedAt,
        archivedAt: null,
        isNew: !p.targetId,
        isModified: !!p.targetId && p.status !== 'to_delete',
        isPendingDelete: p.status === 'to_delete',
        originalData: original || undefined,
        creatorName: p.creatorName,
        status: p.status,
        targetId: p.targetId,
      } as ExternalAccessUnifiedEntry;
    });

    return [...approvedEntries, ...pendingAsUnified].sort(
      (a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime(),
    );
  });

  async function fetchAll() {
    loading.value = true;
    error.value = null;
    try {
      const data = await accessRepository.getAllByStatus('all');
      entries.value = data.filter((entry) => entry.archivedAt === null);
    } catch (err: any) {
      error.value = err.message || 'Не удалось загрузить внешние доступы';
      throw err;
    } finally {
      loading.value = false;
    }
  }

  async function fetchById(id: string) {
    loading.value = true;
    error.value = null;
    try {
      const data = await accessRepository.getById(id);
      const index = entries.value.findIndex((item: Access) => item.id === id);
      if (index >= 0) {
        entries.value[index] = data;
      } else {
        entries.value.push(data);
      }
      return data;
    } catch (err: any) {
      error.value = err.message || 'Не удалось загрузить запись';
      throw err;
    } finally {
      loading.value = false;
    }
  }

  async function fetchPending() {
    loading.value = true;
    error.value = null;
    try {
      const data = await accessRepository.getPending();
      pendingEntries.value = data;
    } catch (err: any) {
      error.value = err.message || 'Не удалось загрузить записи на согласовании';
      throw err;
    } finally {
      loading.value = false;
    }
  }

  async function create(input: CreateAccessInput) {
    loading.value = true;
    error.value = null;
    try {
      await accessRepository.create(input);
      await Promise.all([fetchAll(), fetchPending()]);
    } catch (err: any) {
      error.value = err.message || 'Не удалось создать запись';
      throw err;
    } finally {
      loading.value = false;
    }
  }

  async function update(id: string, input: UpdateAccessInput) {
    loading.value = true;
    error.value = null;
    try {
      await accessRepository.update(id, input);
      await Promise.all([fetchAll(), fetchPending()]);
    } catch (err: any) {
      error.value = err.message || 'Не удалось обновить запись';
      throw err;
    } finally {
      loading.value = false;
    }
  }

  async function remove(id: string) {
    loading.value = true;
    error.value = null;
    try {
      await accessRepository.deleteEntry(id);
      await Promise.all([fetchAll(), fetchPending()]);
    } catch (err: any) {
      error.value = err.message || 'Не удалось удалить запись';
      throw err;
    } finally {
      loading.value = false;
    }
  }

  async function approveEntry(id: string) {
    loading.value = true;
    error.value = null;
    try {
      await accessRepository.approveEntry(id);
      await Promise.all([fetchAll(), fetchPending()]);
    } catch (err: any) {
      error.value = err.message || 'Не удалось согласовать запись';
      throw err;
    } finally {
      loading.value = false;
    }
  }

  async function rejectEntry(id: string) {
    loading.value = true;
    error.value = null;
    try {
      await accessRepository.rejectEntry(id);
      await Promise.all([fetchAll(), fetchPending()]);
    } catch (err: any) {
      error.value = err.message || 'Не удалось отклонить запись';
      throw err;
    } finally {
      loading.value = false;
    }
  }

  function setSearchQuery(query: string) {
    searchQuery.value = query;
  }

  async function handleExternalChange() {
    await Promise.all([fetchAll(), fetchPending()]);
  }

  function bindSocketEvents() {
    if (socketBound.value) return;

    const unsubUpdated = socketService.subscribe(EVENTS.ACCESS_UPDATED, handleExternalChange);
    const unsubCreated = socketService.subscribe(EVENTS.ACCESS_CREATED, handleExternalChange);
    const unsubDeleted = socketService.subscribe(EVENTS.ACCESS_DELETED, handleExternalChange);

    socketUnsubscribers.value = [unsubUpdated, unsubCreated, unsubDeleted];
    socketBound.value = true;
  }

  function unbindSocketEvents() {
    if (!socketBound.value) return;

    socketUnsubscribers.value.forEach((unsub) => unsub());
    socketUnsubscribers.value = [];
    socketBound.value = false;
  }

  function clearError() {
    error.value = null;
  }

  return {
    entries,
    pendingEntries,
    loading,
    error,
    searchQuery,
    items,
    hasPendingEntries,
    unifiedList,
    fetchAll,
    fetchById,
    fetchPending,
    create,
    update,
    remove,
    approveEntry,
    rejectEntry,
    setSearchQuery,
    bindSocketEvents,
    unbindSocketEvents,
    clearError,
  };
});
