import { Injectable, inject } from '@angular/core';
import { Observable, of } from 'rxjs';
import { delay } from 'rxjs/operators';
import { BackofficeDataService } from '../../../../core/backoffice/backoffice-data.service';
import { ProductCardConfig } from '../../../../shared/models/ui';
import { AdminReview, AdminReviewListFilters, AdminReviewsListResult, AdminReviewStatus, AdminReviewTimelineEventKey } from '../models/admin-review.model';
import { AdminReport, AdminReportStatus } from '../models/admin-report.model';
import { AdminModerationGateway } from './admin-moderation-gateway.interface';

const STATUSES: AdminReviewStatus[] = ['pending', 'approved', 'rejected', 'spam'];
const AUTHORS = ['Anna Petrova', 'Karen Sargsyan', 'Ivan Ivanov', 'Mariam Grigoryan', 'Sergey Volkov', 'Lilit Hakobyan'];
const SNIPPETS = [
  'Great quality, exactly as described.',
  'Arrived late and packaging was damaged.',
  'Good value for the price, would buy again.',
  'Not what I expected based on the photos.',
  'Excellent customer service and fast delivery.',
  'Average product, nothing special.',
];
const SEED_COUNT = 32;

@Injectable({ providedIn: 'root' })
export class AdminModerationLocalGateway implements AdminModerationGateway {
  private readonly backofficeData = inject(BackofficeDataService);
  private cache: AdminReview[] | null = null;
  private reportsCache: AdminReport[] | null = null;
  private products: ProductCardConfig[] = [];

  loadReviews(filters: AdminReviewListFilters): Observable<AdminReviewsListResult> {
    return new Observable<AdminReviewsListResult>(subscriber => {
      this.ensureData().then(() => {
        const all = this.cache ?? [];
        const filtered = all
          .filter(review => !filters.search || `${review.customerName} ${review.productName} ${review.text}`.toLowerCase().includes(filters.search.toLowerCase()))
          .filter(review => filters.status === 'all' || review.status === filters.status)
          .filter(review => filters.rating === 'all' || review.rating === filters.rating)
          .sort((a, b) => b.createdAt.localeCompare(a.createdAt));
        const start = (filters.page - 1) * filters.pageSize;
        subscriber.next({
          items: filtered.slice(start, start + filters.pageSize),
          total: filtered.length,
          page: filters.page,
          pageSize: filters.pageSize,
        });
        subscriber.complete();
      });
    }).pipe(delay(50));
  }

  loadReview(id: string): Observable<AdminReview | null> {
    return new Observable<AdminReview | null>(subscriber => {
      this.ensureData().then(() => {
        subscriber.next((this.cache ?? []).find(review => review.id === id) ?? null);
        subscriber.complete();
      });
    });
  }

  setReviewStatus(id: string, status: AdminReviewStatus, note: string): Observable<AdminReview | null> {
    return this.mutate(id, review => ({
      ...review,
      status,
      updatedAt: new Date().toISOString(),
      timeline: [...review.timeline, { eventKey: 'statusChanged' as const, status, actor: 'admin' as const, note, timestamp: new Date().toISOString() }],
    }));
  }

  setReviewVisible(id: string, visible: boolean): Observable<AdminReview | null> {
    const eventKey: AdminReviewTimelineEventKey = visible ? 'restored' : 'hidden';
    return this.mutate(id, review => ({
      ...review,
      visible,
      updatedAt: new Date().toISOString(),
      timeline: [...review.timeline, { eventKey, actor: 'admin' as const, note: '', timestamp: new Date().toISOString() }],
    }));
  }

  setReviewPinned(id: string, pinned: boolean): Observable<AdminReview | null> {
    return this.mutate(id, review => ({ ...review, pinned, updatedAt: new Date().toISOString() }));
  }

  setReviewFeatured(id: string, featured: boolean): Observable<AdminReview | null> {
    return this.mutate(id, review => ({ ...review, featured, updatedAt: new Date().toISOString() }));
  }

  addModeratorNote(id: string, note: string): Observable<AdminReview | null> {
    return this.mutate(id, review => ({
      ...review,
      moderatorNotes: review.moderatorNotes ? `${review.moderatorNotes}\n${note}` : note,
      updatedAt: new Date().toISOString(),
    }));
  }

  deleteReview(id: string): Observable<void> {
    this.cache = (this.cache ?? []).filter(review => review.id !== id);
    return of(void 0).pipe(delay(50));
  }

  loadReports(): Observable<AdminReport[]> {
    return new Observable<AdminReport[]>(subscriber => {
      this.ensureData().then(() => {
        subscriber.next(this.reportsCache ?? []);
        subscriber.complete();
      });
    });
  }

  setReportStatus(id: string, status: AdminReportStatus): Observable<AdminReport | null> {
    const existing = (this.reportsCache ?? []).find(report => report.id === id);
    if (!existing) {
      return of(null);
    }
    const updated = { ...existing, status };
    this.reportsCache = (this.reportsCache ?? []).map(report => report.id === id ? updated : report);
    return of(updated).pipe(delay(50));
  }

  private mutate(id: string, update: (review: AdminReview) => AdminReview): Observable<AdminReview | null> {
    const all = this.cache ?? [];
    const existing = all.find(review => review.id === id);
    if (!existing) {
      return of(null);
    }
    const updated = update(existing);
    this.cache = all.map(review => review.id === id ? updated : review);
    return of(updated).pipe(delay(50));
  }

  private async ensureData(): Promise<void> {
    if (this.cache && this.reportsCache) {
      return;
    }
    // Empty list on transport failure - never leave the promise unsettled.
    this.products = await new Promise<ProductCardConfig[]>(resolve =>
      this.backofficeData.loadProducts().subscribe({ next: value => resolve(value), error: () => resolve([]) })
    );
    this.cache = Array.from({ length: SEED_COUNT }, (_, index) => this.seedReview(index));
    this.reportsCache = this.seedReports();
  }

  private seedReview(index: number): AdminReview {
    const product = this.products[index % Math.max(1, this.products.length)];
    const status = STATUSES[index % STATUSES.length];
    const rating = 1 + (index % 5);
    const createdAt = new Date(Date.now() - index * 20 * 60 * 60 * 1000).toISOString();
    const reportCount = index % 7 === 0 ? 1 + (index % 3) : 0;
    return {
      id: `review-${index + 1}`,
      productId: product?.id ?? '',
      productName: product?.title ?? 'Unknown product',
      customerName: AUTHORS[index % AUTHORS.length],
      customerEmail: `${AUTHORS[index % AUTHORS.length].toLowerCase().replace(/\s+/g, '.')}@example.com`,
      rating,
      text: SNIPPETS[index % SNIPPETS.length],
      photos: index % 5 === 0 ? [product?.imageUrl ?? ''].filter(Boolean) : [],
      status,
      visible: status === 'approved',
      pinned: false,
      featured: false,
      reportCount,
      moderatorNotes: '',
      timeline: [{ eventKey: 'submitted' as const, actor: 'customer' as const, note: '', timestamp: createdAt }],
      createdAt,
      updatedAt: createdAt,
    };
  }

  private seedReports(): AdminReport[] {
    return Array.from({ length: 10 }, (_, index) => {
      const targetTypes: Array<AdminReport['targetType']> = ['review', 'product', 'customer', 'category'];
      const targetType = targetTypes[index % targetTypes.length];
      const product = this.products[index % Math.max(1, this.products.length)];
      const review = this.cache?.[index % (this.cache?.length ?? 1)];
      const targetLabel = targetType === 'review' ? (review?.text.slice(0, 40) ?? 'Unknown review')
        : targetType === 'product' ? (product?.title ?? 'Unknown product')
        : targetType === 'customer' ? (review?.customerName ?? 'Unknown customer')
        : 'Unknown category';
      return {
        id: `report-${index + 1}`,
        targetType,
        targetId: targetType === 'review' ? (review?.id ?? '') : targetType === 'product' ? (product?.id ?? '') : '',
        targetLabel,
        reason: index % 2 === 0 ? 'Inappropriate content' : 'Spam or misleading',
        reporterEmail: `reporter${index + 1}@example.com`,
        status: index % 3 === 0 ? 'resolved' : index % 3 === 1 ? 'dismissed' : 'open',
        createdAt: new Date(Date.now() - index * 30 * 60 * 60 * 1000).toISOString(),
      };
    });
  }
}
