import { Injectable, signal } from '@angular/core';
import { Observable, of, throwError } from 'rxjs';
import {
  MarketplaceRevision,
  canPreview,
  canPublish,
  canValidate,
} from '../models/marketplace-revision.model';
import { MarketplaceRevisionGateway } from './marketplace-revision-gateway.interface';

/**
 * Enforces the §5 pipeline order rather than being a permissive stub - a
 * revision cannot skip validate/preview, and a published revision cannot be
 * re-published. A mock that allows what the real backend rejects teaches the
 * UI a shortcut that will fail against the real API.
 */
@Injectable({ providedIn: 'root' })
export class MarketplaceRevisionLocalGateway implements MarketplaceRevisionGateway {
  private readonly revisions = signal<MarketplaceRevision[]>([]);
  private sequence = 1;

  createDraft(marketplaceId: string): Observable<MarketplaceRevision> {
    const revision: MarketplaceRevision = {
      id: `rev_${this.sequence++}`,
      marketplaceId,
      status: 'draft',
    };
    this.revisions.update(all => [...all, revision]);
    return of(revision);
  }

  validate(marketplaceId: string, revisionId: string): Observable<MarketplaceRevision> {
    // Moves straight to 'preview', not 'validated' - see the model's doc
    // comment: only 3 write endpoints exist for 4 pipeline stages.
    return this.transition(revisionId, 'preview', canValidate, 'must be a draft to validate');
  }

  publish(marketplaceId: string, revisionId: string): Observable<MarketplaceRevision> {
    return this.transition(revisionId, 'published', canPublish, 'must be in preview to publish');
  }

  rollback(marketplaceId: string, revisionId: string): Observable<MarketplaceRevision> {
    const target = this.revisions().find(r => r.id === revisionId);
    if (!target) {
      return throwError(() => new Error(`Revision not found: ${revisionId}`));
    }
    if (target.status !== 'published') {
      return throwError(() => new Error('Only a published revision can be rolled back from.'));
    }

    // §5: rollback creates a NEW revision, never mutates the old one.
    const rolledBack: MarketplaceRevision = {
      id: `rev_${this.sequence++}`,
      marketplaceId,
      status: 'published',
      publishedAt: new Date().toISOString(),
      supersedesRevisionId: target.id,
    };
    this.revisions.update(all => [...all, rolledBack]);
    return of(rolledBack);
  }

  private transition(
    revisionId: string,
    to: MarketplaceRevision['status'],
    guard: (revision: MarketplaceRevision) => boolean,
    errorMessage: string,
  ): Observable<MarketplaceRevision> {
    const existing = this.revisions().find(r => r.id === revisionId);
    if (!existing) {
      return throwError(() => new Error(`Revision not found: ${revisionId}`));
    }
    if (!guard(existing)) {
      return throwError(() => new Error(errorMessage));
    }

    const updated: MarketplaceRevision = {
      ...existing,
      status: to,
      ...(to === 'published' ? { publishedAt: new Date().toISOString() } : {}),
    };
    this.revisions.update(all => all.map(r => (r.id === revisionId ? updated : r)));
    return of(updated);
  }
}
