import { TestBed } from '@angular/core/testing';
import { of, throwError } from 'rxjs';
import { AdminOrdersFacade } from './admin-orders.facade';
import { ADMIN_ORDERS_GATEWAY } from '../services/admin-orders-gateway.token';
import { AdminOrdersGateway } from '../services/admin-orders-gateway.interface';
import { AdminOrder } from '../models/admin-order.model';
import { LocalStorageService } from '../../../../core/storage/local-storage.service';

function order(overrides: Partial<AdminOrder> = {}): AdminOrder {
  return {
    id: 'o1',
    orderNumber: '1001',
    status: 'pending',
    customer: { name: 'Buyer', email: 'b@example.com', phone: '' },
    payment: { method: 'card', status: 'paid', amount: 1000, currency: 'RUB' },
    shipping: { address: '', method: '', trackingNumber: '' },
    items: [],
    total: 1000,
    currency: 'RUB',
    notes: '',
    internalNotes: '',
    timeline: [],
    archived: false,
    createdAt: new Date().toISOString(),
    updatedAt: new Date().toISOString(),
    ...overrides,
  };
}

describe('AdminOrdersFacade', () => {
  let facade: AdminOrdersFacade;
  let gateway: jasmine.SpyObj<AdminOrdersGateway>;

  beforeEach(() => {
    gateway = jasmine.createSpyObj<AdminOrdersGateway>('AdminOrdersGateway', [
      'loadOrders', 'loadOrder', 'updateStatus', 'requestRefund', 'addNote',
      'archiveOrder', 'restoreOrder', 'deleteOrder',
    ]);

    TestBed.configureTestingModule({
      providers: [
        { provide: ADMIN_ORDERS_GATEWAY, useValue: gateway },
        { provide: LocalStorageService, useValue: { getItem: () => null, setItem: () => {}, getJSON: () => null, setJSON: () => {} } },
      ],
    });

    facade = TestBed.inject(AdminOrdersFacade);
  });

  describe('loadList', () => {
    it('populates orders and total on success', () => {
      gateway.loadOrders.and.returnValue(of({ items: [order()], total: 1, page: 1, pageSize: 10 }));

      facade.loadList();

      expect(facade.orders().length).toBe(1);
      expect(facade.total()).toBe(1);
      expect(facade.loading()).toBeFalse();
      expect(facade.error()).toBeNull();
    });

    it('clears the list and surfaces an error on failure, rather than leaving stale data on screen', () => {
      gateway.loadOrders.and.returnValue(of({ items: [order()], total: 1, page: 1, pageSize: 10 }));
      facade.loadList();

      gateway.loadOrders.and.returnValue(throwError(() => new Error('network')));
      facade.loadList();

      expect(facade.orders()).toEqual([]);
      expect(facade.total()).toBe(0);
      expect(facade.error()).toBe('common.errorDescription');
    });
  });

  describe('updateFilters', () => {
    it('resets to page 1 whenever a filter other than page changes', () => {
      gateway.loadOrders.and.returnValue(of({ items: [], total: 0, page: 1, pageSize: 10 }));

      facade.updateFilters({ page: 5 });
      facade.updateFilters({ status: 'processing' });

      expect(facade.filters().page).toBe(1);
      expect(facade.filters().status).toBe('processing');
    });

    it('reloads the list on every filter change', () => {
      gateway.loadOrders.and.returnValue(of({ items: [], total: 0, page: 1, pageSize: 10 }));

      facade.updateFilters({ search: 'x' });

      expect(gateway.loadOrders).toHaveBeenCalled();
    });
  });

  describe('addNote', () => {
    it('does not call the gateway for a whitespace-only note', () => {
      facade.addNote('o1', '   ', false);

      expect(gateway.addNote).not.toHaveBeenCalled();
    });

    it('trims the note before sending it', () => {
      gateway.addNote.and.returnValue(of(order()));

      facade.addNote('o1', '  hello  ', true);

      expect(gateway.addNote).toHaveBeenCalledWith('o1', 'hello', true);
    });
  });

  describe('selection', () => {
    it('toggleAll selects every currently loaded order id', () => {
      gateway.loadOrders.and.returnValue(of({ items: [order({ id: 'a' }), order({ id: 'b' })], total: 2, page: 1, pageSize: 10 }));
      facade.loadList();

      facade.toggleAll(true);

      expect(facade.selectedIds()).toEqual(['a', 'b']);
      expect(facade.hasSelection()).toBeTrue();
    });

    it('toggleSelection does not duplicate an id already selected', () => {
      facade.toggleSelection('a', true);
      facade.toggleSelection('a', true);

      expect(facade.selectedIds()).toEqual(['a']);
    });

    it('clearSelection empties the selection', () => {
      facade.toggleSelection('a', true);
      facade.clearSelection();

      expect(facade.hasSelection()).toBeFalse();
    });
  });

  describe('bulk actions', () => {
    it('applyBulkStatus calls updateStatus for every selected id then clears selection', () => {
      gateway.updateStatus.and.returnValue(of(order()));
      gateway.loadOrders.and.returnValue(of({ items: [], total: 0, page: 1, pageSize: 10 }));
      facade.toggleSelection('a', true);
      facade.toggleSelection('b', true);

      facade.applyBulkStatus('cancelled');

      expect(gateway.updateStatus).toHaveBeenCalledWith('a', 'cancelled');
      expect(gateway.updateStatus).toHaveBeenCalledWith('b', 'cancelled');
      expect(facade.hasSelection()).toBeFalse();
    });

    it('applyBulkArchive routes to archiveOrder when archiving true, restoreOrder when false', () => {
      gateway.archiveOrder.and.returnValue(of(order()));
      gateway.restoreOrder.and.returnValue(of(order()));
      gateway.loadOrders.and.returnValue(of({ items: [], total: 0, page: 1, pageSize: 10 }));

      facade.toggleSelection('a', true);
      facade.applyBulkArchive(true);
      expect(gateway.archiveOrder).toHaveBeenCalledWith('a');

      facade.toggleSelection('b', true);
      facade.applyBulkArchive(false);
      expect(gateway.restoreOrder).toHaveBeenCalledWith('b');
    });
  });

  describe('computeDashboardStats (via loadDashboardStats)', () => {
    it('counts a customer with more than one order as returning', () => {
      gateway.loadOrders.and.returnValue(of({
        items: [
          order({ id: 'a', customer: { name: 'X', email: 'x@example.com', phone: '' } }),
          order({ id: 'b', customer: { name: 'X', email: 'x@example.com', phone: '' } }),
          order({ id: 'c', customer: { name: 'Y', email: 'y@example.com', phone: '' } }),
        ],
        total: 3, page: 1, pageSize: 100000,
      }));

      facade.loadDashboardStats();

      expect(facade.dashboardStats()?.customers).toBe(2);
      expect(facade.dashboardStats()?.returningCustomers).toBe(1);
    });

    it('flags refund_requested payments as an alert', () => {
      gateway.loadOrders.and.returnValue(of({
        items: [order({ payment: { method: 'card', status: 'refund_requested', amount: 500, currency: 'RUB' } })],
        total: 1, page: 1, pageSize: 100000,
      }));

      facade.loadDashboardStats();

      expect(facade.dashboardStats()?.alerts.some(a => a.labelKey === 'adminOrders.alertRefundRequested')).toBeTrue();
    });

    it('flags a pending order older than 48h as stuck', () => {
      const old = new Date(Date.now() - 49 * 60 * 60 * 1000).toISOString();
      gateway.loadOrders.and.returnValue(of({
        items: [order({ status: 'pending', createdAt: old })],
        total: 1, page: 1, pageSize: 100000,
      }));

      facade.loadDashboardStats();

      expect(facade.dashboardStats()?.alerts.some(a => a.labelKey === 'adminOrders.alertStuckPending')).toBeTrue();
    });

    it('does not flag a pending order under 48h old', () => {
      const recent = new Date(Date.now() - 1 * 60 * 60 * 1000).toISOString();
      gateway.loadOrders.and.returnValue(of({
        items: [order({ status: 'pending', createdAt: recent })],
        total: 1, page: 1, pageSize: 100000,
      }));

      facade.loadDashboardStats();

      expect(facade.dashboardStats()?.alerts.some(a => a.labelKey === 'adminOrders.alertStuckPending')).toBeFalse();
    });

    it('averageOrder is 0 for an empty order book, not NaN or a divide-by-zero crash', () => {
      gateway.loadOrders.and.returnValue(of({ items: [], total: 0, page: 1, pageSize: 100000 }));

      facade.loadDashboardStats();

      expect(facade.dashboardStats()?.averageOrder).toBe(0);
    });
  });

  describe('exportCsv', () => {
    it('produces one header row plus one row per loaded order', () => {
      gateway.loadOrders.and.returnValue(of({ items: [order(), order({ id: 'o2', orderNumber: '1002' })], total: 2, page: 1, pageSize: 10 }));
      facade.loadList();

      const csv = facade.exportCsv();

      expect(csv.split('\n').length).toBe(3);
    });
  });
});
