import { describe, it, expect, beforeEach, vi } from 'vitest';
import { setActivePinia, createPinia } from 'pinia';
import { useListDetailStore, ListWithExpenses } from '../listDetailStore'; // Adjust path
import { apiClient } from '@/services/api'; // Adjust path
import type { Expense, ExpenseSplit, SettlementActivity, SettlementActivityCreate, UserPublic } from '@/types/expense';
import { ExpenseSplitStatusEnum, ExpenseOverallStatusEnum } from '@/types/expense';
import type { List } from '@/types/list';

// Mock the apiClient
vi.mock('@/services/api', () => ({
  apiClient: {
    get: vi.fn(),
    post: vi.fn(), // Used by settleExpenseSplit if it were calling the real API
    settleExpenseSplit: vi.fn() // Specifically mock this for the action
  },
  API_ENDPOINTS: { // Mock endpoints if store uses them directly for URL construction
    LISTS: {
      BY_ID: (id: string) => `/lists/${id}` // Example
    }
  }
}));

describe('listDetailStore', () => {
  beforeEach(() => {
    setActivePinia(createPinia());
    // Reset mocks before each test
    vi.clearAllMocks();
  });

  describe('actions', () => {
    describe('settleExpenseSplit', () => {
      it('handles successful settlement, sets loading states, and refetches list details', async () => {
        const store = useListDetailStore();
        const listId = '123';
        const splitId = 1;
        const mockActivityData: SettlementActivityCreate = {
          expense_split_id: splitId,
          paid_by_user_id: 100,
          amount_paid: '10.00',
        };
        const mockApiResponse = { id: 1, ...mockActivityData, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), paid_at: new Date().toISOString() };

        // Mock the settleExpenseSplit API call (simulated as per store logic)
        // In the store, this is currently a console.warn and a promise resolve.
        // We are testing the action's behavior *around* this (mocked) call.
        
        // Spy on fetchListWithExpenses to ensure it's called
        const fetchSpy = vi.spyOn(store, 'fetchListWithExpenses');

        store.currentList = { id: parseInt(listId), name: 'Test List', expenses: [] } as ListWithExpenses; // Ensure currentList is set for refetch logic

        expect(store.isSettlingSplit).toBe(false);
        const resultPromise = store.settleExpenseSplit({
          list_id_for_refetch: listId,
          expense_split_id: splitId,
          activity_data: mockActivityData,
        });

        expect(store.isSettlingSplit).toBe(true); // Check loading state during call
        
        const result = await resultPromise;

        expect(result).toBe(true); // Action indicates success
        expect(store.isSettlingSplit).toBe(false); // Loading state reset
        expect(store.error).toBeNull();
        expect(fetchSpy).toHaveBeenCalledWith(listId);
      });

      it('handles failed settlement and sets error state', async () => {
        const store = useListDetailStore();
        const listId = '123';
        const splitId = 1;
        const mockActivityData: SettlementActivityCreate = {
          expense_split_id: splitId,
          paid_by_user_id: 100,
          amount_paid: '10.00',
        };
        const errorMessage = 'Network Error';

        // For this test, we need to make the *simulated* part of the store action throw an error.
        // Since the actual API call is commented out, we can't mock apiClient.settleExpenseSplit to throw.
        // Instead, we can mock fetchListWithExpenses to throw, as that's called after the simulated success.
        // Or, modify the store action slightly for testability if real API call was there.
        // For current store code: the action itself doesn't use apiClient.settleExpenseSplit.
        // Let's assume for testing the actual API call, we'd mock apiClient.settleExpenseSplit.
        
        // To test the catch block of settleExpenseSplit, we make the placeholder promise reject.
        // This requires modifying the store or making the test more complex.
        // Given the store currently *always* resolves the placeholder, we'll simulate error via fetchListWithExpenses.
        
        vi.spyOn(store, 'fetchListWithExpenses').mockRejectedValueOnce(new Error(errorMessage));
        store.currentList = { id: parseInt(listId), name: 'Test List', expenses: [] } as ListWithExpenses;


        expect(store.isSettlingSplit).toBe(false);
        const result = await store.settleExpenseSplit({
          list_id_for_refetch: listId,
          expense_split_id: splitId,
          activity_data: mockActivityData,
        });
        
        expect(result).toBe(false); // Action indicates failure
        expect(store.isSettlingSplit).toBe(false);
        // The error is set by fetchListWithExpenses in this simulation
        // If settleExpenseSplit itself failed, its catch block would set store.error
        // expect(store.error).toBe(errorMessage); // This depends on how error is propagated
      });
    });
  });

  describe('getters', () => {
    it('getPaidAmountForSplit calculates correctly', () => {
      const store = useListDetailStore();
      const mockUser: UserPublic = { id: 1, name: 'User 1', email: 'u1@e.com' };
      store.currentList = {
        id: 1,
        name: 'Test List',
        expenses: [
          {
            id: 10,
            description: 'Dinner',
            total_amount: '100.00',
            currency: 'USD',
            expense_date: new Date().toISOString(),
            split_type: 'EQUAL',
            paid_by_user_id: 1,
            created_by_user_id: 1,
            version: 1,
            created_at: new Date().toISOString(),
            updated_at: new Date().toISOString(),
            overall_settlement_status: ExpenseOverallStatusEnum.UNPAID,
            splits: [
              {
                id: 101, expense_id: 10, user_id: 1, owed_amount: '50.00', status: ExpenseSplitStatusEnum.UNPAID, settlement_activities: [
                  { id: 1001, expense_split_id: 101, paid_by_user_id: 1, amount_paid: '20.00', paid_at: new Date().toISOString(), created_by_user_id: 1, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), payer: mockUser, creator: mockUser },
                  { id: 1002, expense_split_id: 101, paid_by_user_id: 1, amount_paid: '15.50', paid_at: new Date().toISOString(), created_by_user_id: 1, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), payer: mockUser, creator: mockUser },
                ], user: mockUser 
              },
              { id: 102, expense_id: 10, user_id: 2, owed_amount: '50.00', status: ExpenseSplitStatusEnum.UNPAID, settlement_activities: [], user: {id: 2, name: 'User 2', email: 'u2@e.com'} },
            ],
          },
        ],
      } as ListWithExpenses;

      expect(store.getPaidAmountForSplit(101)).toBe(35.50);
      expect(store.getPaidAmountForSplit(102)).toBe(0);
      expect(store.getPaidAmountForSplit(999)).toBe(0); // Non-existent split
    });

    it('getExpenseSplitById returns correct split or undefined', () => {
      const store = useListDetailStore();
      const mockSplit1: ExpenseSplit = { id: 101, expense_id: 10, user_id: 1, owed_amount: '50.00', status: ExpenseSplitStatusEnum.UNPAID, settlement_activities: [], created_at: '', updated_at: '' };
      const mockSplit2: ExpenseSplit = { id: 102, expense_id: 10, user_id: 2, owed_amount: '50.00', status: ExpenseSplitStatusEnum.UNPAID, settlement_activities: [], created_at: '', updated_at: '' };
      store.currentList = {
        id: 1, name: 'Test List', expenses: [
          { 
            id: 10, description: 'Test Expense', total_amount: '100.00', splits: [mockSplit1, mockSplit2],
            currency: 'USD', expense_date: '', split_type: 'EQUAL', paid_by_user_id: 1, created_by_user_id: 1, version: 1, created_at: '', updated_at: '', overall_settlement_status: ExpenseOverallStatusEnum.UNPAID
          }
        ]
      } as ListWithExpenses;
      
      expect(store.getExpenseSplitById(101)).toEqual(mockSplit1);
      expect(store.getExpenseSplitById(102)).toEqual(mockSplit2);
      expect(store.getExpenseSplitById(999)).toBeUndefined();
    });
  });
});
