import { TestBed } from '@angular/core/testing';
import { provideRouter } from '@angular/router';
import { provideHttpClient } from '@angular/common/http';
import { provideHttpClientTesting } from '@angular/common/http/testing';
import { of } from 'rxjs';
import { BootstrapConfig } from '../../../shared/models/config';
import { CONFIG_PROVIDER } from '../../../core/config/config-provider.token';
import { PlatformRuntimeService } from '../../../core/runtime/platform-runtime.service';
import { ProjectEditorFacade } from './project-editor.facade';

function makeBootstrap(): BootstrapConfig {
  return {
    tenant: { id: 'tenant-1', defaultLocale: 'en', supportedLocales: ['en'], websiteBaseUrl: 'https://dexar.market' },
    branding: { logoUrl: 'logo.png' },
    localization: { supportedLocales: ['en'], defaultLocale: 'en', currencyByLocale: {}, dictionaries: [] },
    staticPages: {},
    pages: [
      {
        key: 'home',
        route: { path: '/' },
        sections: [
          {
            order: 0,
            layout: { strategy: 'stack' },
            widgets: [{ id: 'w1', type: 'hero', version: '1', props: {} }],
          },
        ],
      },
    ],
    navigation: { header: [], footer: [] },
    theme: {
      palette: {
        primary: '#497671', secondary: '#a1b4b5', accent: '#a7ceca',
        success: '#10b981', warning: '#f59e0b', danger: '#ef4444', info: '#3b82f6',
        textPrimary: '#1e3c38', textSecondary: '#667a77',
        backgroundPrimary: '#ffffff', backgroundSecondary: '#f5f5f5', border: '#d3dad9',
      },
    },
    layout: { type: 'default' },
  } as unknown as BootstrapConfig;
}

describe('ProjectEditorFacade', () => {
  let facade: ProjectEditorFacade;
  let runtimeSpy: jasmine.SpyObj<PlatformRuntimeService>;

  beforeEach(() => {
    localStorage.clear();
    runtimeSpy = jasmine.createSpyObj<PlatformRuntimeService>('PlatformRuntimeService', ['reloadFromBootstrap']);

    TestBed.configureTestingModule({
      providers: [
        provideRouter([]), provideHttpClient(), provideHttpClientTesting(),
        { provide: CONFIG_PROVIDER, useValue: { loadBootstrap: () => of(makeBootstrap()) } },
        { provide: PlatformRuntimeService, useValue: runtimeSpy },
      ],
    });

    facade = TestBed.inject(ProjectEditorFacade);
    facade.loadBootstrap();
  });

  afterEach(() => localStorage.clear());

  it('loads the bootstrap and starts with a clean undo/redo stack', () => {
    expect(facade.bootstrap()).toBeTruthy();
    expect(facade.canUndo()).toBeFalse();
    expect(facade.canRedo()).toBeFalse();
  });

  describe('undo/redo', () => {
    it('undo reverts the last edit and enables redo', (done) => {
      facade.updateBootstrap(current => ({ ...current, branding: { ...current.branding, logoUrl: 'new-logo.png' } }));

      // updateBootstrap debounces the history commit - wait past HISTORY_DEBOUNCE_MS (300ms).
      setTimeout(() => {
        expect(facade.bootstrap()?.branding.logoUrl).toBe('new-logo.png');
        expect(facade.canUndo()).toBeTrue();

        facade.undo();
        expect(facade.bootstrap()?.branding.logoUrl).toBe('logo.png');
        expect(facade.canUndo()).toBeFalse();
        expect(facade.canRedo()).toBeTrue();

        facade.redo();
        expect(facade.bootstrap()?.branding.logoUrl).toBe('new-logo.png');
        expect(facade.canRedo()).toBeFalse();
        done();
      }, 350);
    });

    it('a fresh edit after undo discards the redo stack', (done) => {
      facade.updateBootstrap(current => ({ ...current, branding: { ...current.branding, logoUrl: 'v2.png' } }));

      setTimeout(() => {
        facade.undo();
        expect(facade.canRedo()).toBeTrue();

        facade.updateBootstrap(current => ({ ...current, branding: { ...current.branding, logoUrl: 'v3.png' } }));
        expect(facade.canRedo()).toBeFalse();
        done();
      }, 350);
    });
  });

  describe('draft persistence round-trip', () => {
    it('persists edits to the draft store and restores them on next load', (done) => {
      facade.updateBootstrap(current => ({ ...current, branding: { ...current.branding, logoUrl: 'draft-logo.png' } }));

      setTimeout(() => {
        // Simulate a fresh facade instance (e.g. page reload) picking up the persisted draft.
        TestBed.resetTestingModule();
        TestBed.configureTestingModule({
          providers: [
            provideRouter([]), provideHttpClient(), provideHttpClientTesting(),
            { provide: CONFIG_PROVIDER, useValue: { loadBootstrap: () => of(makeBootstrap()) } },
            { provide: PlatformRuntimeService, useValue: runtimeSpy },
          ],
        });
        const reloaded = TestBed.inject(ProjectEditorFacade);
        reloaded.loadBootstrap();

        expect(reloaded.draftRestored()).toBeTrue();
        expect(reloaded.bootstrap()?.branding.logoUrl).toBe('draft-logo.png');
        done();
      }, 350);
    });
  });

  describe('publish gating', () => {
    it('publishes when there are no blocking validation issues', () => {
      expect(facade.hasBlockingIssues()).toBeFalse();
      const result = facade.publish();
      expect(result).toBeTrue();
      expect(facade.status()).toBe('published');
      expect(runtimeSpy.reloadFromBootstrap).toHaveBeenCalled();
    });

    it('refuses to publish while a blocking issue is present', () => {
      facade.updateBootstrap(current => ({ ...current, branding: { ...current.branding, logoUrl: '' } }));

      expect(facade.hasBlockingIssues()).toBeTrue();
      const result = facade.publish();
      expect(result).toBeFalse();
      expect(facade.status()).not.toBe('published');
      expect(runtimeSpy.reloadFromBootstrap).not.toHaveBeenCalled();
    });
  });
});
