import { TestBed } from '@angular/core/testing';
import { of, throwError } from 'rxjs';
import { AdminProductsFacade } from './admin-products.facade';
import { ADMIN_PRODUCTS_GATEWAY } from '../services/admin-products-gateway.token';
import { AdminProductsGateway } from '../services/admin-products-gateway.interface';
import { AdminProductsFormFactory } from '../services/admin-products-form.factory';
import { ProjectEditorFacade } from '../../../project-editor/facade/project-editor.facade';
import { LocalStorageService } from '../../../../core/storage/local-storage.service';
import { AdminProduct } from '../models/admin-product.model';

function product(overrides: Partial<AdminProduct> = {}): AdminProduct {
  return {
    id: 'p1',
    name: 'Widget',
    slug: 'widget',
    sku: 'W-1',
    barcode: '',
    brand: '',
    categoryId: 'c1',
    visible: true,
    archived: false,
    priority: 0,
    media: { images: ['a.jpg'], gallery: [], videos: [] },
    price: 1000,
    discount: 0,
    currency: 'RUB',
    quantity: 10,
    stockStatus: 'in_stock',
    availability: '',
    shortDescription: 'A widget',
    htmlDescription: '',
    specifications: [],
    attributes: [],
    variantAttributes: [],
    visits: 0,
    variants: [],
    relatedProductIds: [],
    translations: {},
    seo: { metaTitle: 'Widget', metaDescription: 'A fine widget', keywords: '' },
    featured: false,
    recommended: false,
    isNew: false,
    bestseller: false,
    badges: [],
    reviews: [],
    questions: [],
    createdAt: new Date().toISOString(),
    updatedAt: new Date().toISOString(),
    ...overrides,
  } as AdminProduct;
}

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

  beforeEach(() => {
    gateway = jasmine.createSpyObj<AdminProductsGateway>('AdminProductsGateway', [
      'loadProducts', 'loadProduct', 'loadCategories', 'createProduct', 'updateProduct',
      'deleteProduct', 'duplicateProduct', 'archiveProduct', 'restoreProduct',
    ]);

    TestBed.configureTestingModule({
      providers: [
        { provide: ADMIN_PRODUCTS_GATEWAY, useValue: gateway },
        { provide: AdminProductsFormFactory, useValue: { createEmpty: () => product({ id: '' }) } },
        { provide: ProjectEditorFacade, useValue: { bootstrap: () => null, loadBootstrap: () => {} } },
        { provide: LocalStorageService, useValue: { getItem: () => null, setItem: () => {}, getJSON: () => null, setJSON: () => {} } },
      ],
    });

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

  describe('health', () => {
    it('scores 100% when every check passes', () => {
      const result = facade.health(product());

      expect(result.completionPercent).toBe(100);
    });

    it('flags a missing SEO description even when the title is present', () => {
      const result = facade.health(product({ seo: { metaTitle: 'x', metaDescription: '  ', keywords: '' } }));

      expect(result.hasSeo).toBeFalse();
    });

    it('counts either images or gallery as satisfying hasImages', () => {
      expect(facade.health(product({ media: { images: [], gallery: ['g.jpg'], videos: [] } })).hasImages).toBeTrue();
      expect(facade.health(product({ media: { images: [], gallery: [], videos: [] } })).hasImages).toBeFalse();
    });

    it('treats a zero price as missing, not a valid free product', () => {
      expect(facade.health(product({ price: 0 })).hasPrice).toBeFalse();
    });
  });

  describe('computeDashboardStats (via loadDashboardStats)', () => {
    it('recommends adding images before anything else, when any product lacks them', () => {
      gateway.loadProducts.and.returnValue(of({
        items: [product({ id: 'a', media: { images: [], gallery: [], videos: [] } })],
        total: 1, page: 1, pageSize: 100000,
      }));

      facade.loadDashboardStats();

      expect(facade.dashboardStats()?.recommendation.labelKey).toBe('adminProducts.recommendAddImages');
      expect(facade.dashboardStats()?.recommendation.productId).toBe('a');
    });

    it('falls through to restock recommendation once images and SEO are fine', () => {
      gateway.loadProducts.and.returnValue(of({
        items: [product({ id: 'a', stockStatus: 'out_of_stock' })],
        total: 1, page: 1, pageSize: 100000,
      }));

      facade.loadDashboardStats();

      expect(facade.dashboardStats()?.recommendation.labelKey).toBe('adminProducts.recommendRestock');
    });

    it('recommends nothing when the catalog has no issues', () => {
      gateway.loadProducts.and.returnValue(of({ items: [product()], total: 1, page: 1, pageSize: 100000 }));

      facade.loadDashboardStats();

      expect(facade.dashboardStats()?.recommendation.labelKey).toBe('adminProducts.recommendNone');
    });

    it('counts published as visible and not archived, distinct from drafts', () => {
      gateway.loadProducts.and.returnValue(of({
        items: [
          product({ id: 'a', visible: true, archived: false }),
          product({ id: 'b', visible: false, archived: false }),
          product({ id: 'c', visible: true, archived: true }),
        ],
        total: 3, page: 1, pageSize: 100000,
      }));

      facade.loadDashboardStats();

      expect(facade.dashboardStats()?.published).toBe(1);
      expect(facade.dashboardStats()?.drafts).toBe(1);
    });
  });

  describe('saveDraft', () => {
    it('calls createProduct in create mode', () => {
      gateway.createProduct.and.returnValue(of(product()));
      gateway.loadProducts.and.returnValue(of({ items: [], total: 0, page: 1, pageSize: 10 }));
      facade.startCreate();

      facade.saveDraft();

      expect(gateway.createProduct).toHaveBeenCalled();
      expect(gateway.updateProduct).not.toHaveBeenCalled();
    });

    it('calls updateProduct in edit mode', () => {
      gateway.loadProduct.and.returnValue(of(product()));
      gateway.updateProduct.and.returnValue(of(product()));
      gateway.loadProducts.and.returnValue(of({ items: [], total: 0, page: 1, pageSize: 10 }));
      facade.loadForEdit('p1', 'edit');

      facade.saveDraft();

      expect(gateway.updateProduct).toHaveBeenCalled();
      expect(gateway.createProduct).not.toHaveBeenCalled();
    });

    it('does nothing when there is no draft loaded', () => {
      facade.saveDraft();

      expect(gateway.createProduct).not.toHaveBeenCalled();
      expect(gateway.updateProduct).not.toHaveBeenCalled();
    });

    it('sets mutationError on failure rather than silently discarding the draft', () => {
      gateway.createProduct.and.returnValue(throwError(() => new Error('x')));
      facade.startCreate();

      facade.saveDraft();

      expect(facade.mutationError()).toBe('common.errorDescription');
    });

    it('calls onSuccess only after the save actually succeeds', () => {
      gateway.createProduct.and.returnValue(of(product()));
      gateway.loadProducts.and.returnValue(of({ items: [], total: 0, page: 1, pageSize: 10 }));
      facade.startCreate();
      const onSuccess = jasmine.createSpy('onSuccess');

      facade.saveDraft(onSuccess);

      expect(onSuccess).toHaveBeenCalled();
    });
  });

  describe('updateDraft', () => {
    it('marks the facade dirty and applies the patch', () => {
      gateway.loadProducts.and.returnValue(of({ items: [], total: 0, page: 1, pageSize: 10 }));
      facade.startCreate();

      facade.updateDraft({ name: 'Renamed' });

      expect(facade.dirty()).toBeTrue();
      expect(facade.draft()?.name).toBe('Renamed');
    });

    it('stamps updatedAt with a valid ISO timestamp', () => {
      facade.startCreate();

      facade.updateDraft({ name: 'Renamed' });

      expect(new Date(facade.draft()!.updatedAt).toString()).not.toBe('Invalid Date');
    });
  });

  describe('loadList error handling', () => {
    it('clears the list and does not throw when the gateway errors', () => {
      gateway.loadProducts.and.returnValue(throwError(() => new Error('x')));

      expect(() => facade.loadList()).not.toThrow();
    });
  });
});
