import { Injectable } 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 { AdminProduct, AdminProductCategoryOption, AdminProductListFilters, AdminProductsListResult } from '../models/admin-product.model';
import { AdminProductsGateway } from './admin-products-gateway.interface';
import { AdminCategoriesLocalGateway } from '../../categories/services/admin-categories-local.gateway';

@Injectable({ providedIn: 'root' })
export class AdminProductsLocalGateway implements AdminProductsGateway {
  private productsCache: AdminProduct[] | null = null;
  private categoriesCache: AdminProductCategoryOption[] | null = null;

  constructor(
    private readonly backofficeData: BackofficeDataService,
    private readonly categoriesGateway: AdminCategoriesLocalGateway
  ) {}

  loadProducts(filters: AdminProductListFilters): Observable<AdminProductsListResult> {
    return new Observable<AdminProductsListResult>(subscriber => {
      this.ensureData().then(() => {
        const all = this.productsCache ?? [];
        const filtered = all
          .filter(product => filters.includeArchived || !product.archived)
          .filter(product => !filters.search || `${product.name} ${product.sku} ${product.brand}`.toLowerCase().includes(filters.search.toLowerCase()))
          .filter(product => !filters.categoryId || product.categoryId === filters.categoryId)
          .filter(product => filters.visibility === 'all' || (filters.visibility === 'visible' ? product.visible : !product.visible))
          .filter(product => filters.stock === 'all' || product.stockStatus === filters.stock)
          .sort((left, right) => this.compare(left, right, filters.sort));
        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));
  }

  loadProduct(id: string): Observable<AdminProduct | null> {
    return new Observable<AdminProduct | null>(subscriber => {
      this.ensureData().then(() => {
        subscriber.next(this.productsCache?.find(product => product.id === id) ?? null);
        subscriber.complete();
      });
    });
  }

  loadCategories(): Observable<AdminProductCategoryOption[]> {
    return new Observable<AdminProductCategoryOption[]>(subscriber => {
      this.ensureData().then(() => {
        subscriber.next(this.categoriesCache ?? []);
        subscriber.complete();
      });
    });
  }

  createProduct(product: AdminProduct): Observable<AdminProduct> {
    this.productsCache = [product, ...(this.productsCache ?? [])];
    return of(product).pipe(delay(50));
  }

  updateProduct(product: AdminProduct): Observable<AdminProduct> {
    this.productsCache = (this.productsCache ?? []).map(item => item.id === product.id ? product : item);
    return of(product).pipe(delay(50));
  }

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

  duplicateProduct(id: string): Observable<AdminProduct | null> {
    const source = (this.productsCache ?? []).find(product => product.id === id);
    if (!source) {
      return of(null);
    }

    const duplicated: AdminProduct = {
      ...source,
      archived: false,
      id: `${source.id}-copy-${Date.now()}`,
      sku: `${source.sku}-COPY`,
      slug: `${source.slug}-copy-${Date.now()}`,
      name: `${source.name} Copy`,
      visits: 0,
      createdAt: new Date().toISOString(),
      updatedAt: new Date().toISOString(),
    };
    this.productsCache = [duplicated, ...(this.productsCache ?? [])];
    return of(duplicated).pipe(delay(50));
  }

  archiveProduct(id: string): Observable<void> {
    this.productsCache = (this.productsCache ?? []).map(item => item.id === id ? { ...item, archived: true, updatedAt: new Date().toISOString() } : item);
    return of(void 0).pipe(delay(50));
  }

  restoreProduct(id: string): Observable<AdminProduct | null> {
    const restored = (this.productsCache ?? []).find(item => item.id === id);
    if (!restored) {
      return of(null);
    }
    const updated = { ...restored, archived: false, updatedAt: new Date().toISOString() };
    this.productsCache = (this.productsCache ?? []).map(item => item.id === id ? updated : item);
    return of(updated).pipe(delay(50));
  }

  private async ensureData(): Promise<void> {
    if (this.productsCache && this.categoriesCache) {
      return;
    }

    // Resolve with an empty list on transport failure so callers see an empty
    // state instead of a promise that never settles (which froze every admin
    // list in a permanent loading state when the API was unreachable).
    const products = await new Promise<ProductCardConfig[]>(resolve =>
      this.backofficeData.loadProducts().subscribe({ next: value => resolve(value), error: () => resolve([]) })
    );
    const categories = await new Promise<AdminProductCategoryOption[]>(resolve =>
      this.categoriesGateway.loadCategories({ search: '', visibility: 'all', includeDeleted: false })
        .subscribe({
          next: value => resolve(value.map(category => ({ id: category.id, title: category.title }))),
          error: () => resolve([]),
        })
    );
    this.productsCache = products.map((product, index) => this.toAdminProduct(product, categories[index % Math.max(1, categories.length)]?.id ?? 'cat-001'));
    this.categoriesCache = categories;
  }

  private toAdminProduct(product: ProductCardConfig, categoryId: string): AdminProduct {
    return {
      id: product.id,
      name: product.title,
      slug: product.title.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, ''),
      sku: product.sku,
      barcode: '',
      brand: product.subtitle ?? 'Default Brand',
      categoryId,
      visible: true,
      archived: false,
      priority: 0,
      media: {
        images: [product.imageUrl],
        gallery: [product.imageUrl],
        videos: [],
      },
      price: product.price.amount,
      discount: product.price.originalAmount && product.price.originalAmount > product.price.amount
        ? Math.round((1 - product.price.amount / product.price.originalAmount) * 100)
        : 0,
      currency: product.price.currency,
      quantity: product.stockStatus === 'out_of_stock' ? 0 : product.stockStatus === 'low_stock' ? 3 : 25,
      visits: 0,
      stockStatus: product.stockStatus ?? 'in_stock',
      availability: product.stockStatus ?? 'in_stock',
      shortDescription: product.subtitle ?? '',
      htmlDescription: `<p>${product.subtitle ?? product.title}</p>`,
      specifications: [],
      attributes: [],
      variantAttributes: [], variants: [],
      relatedProductIds: [],
      translations: {
        en: { name: product.title, shortDescription: product.subtitle ?? '', htmlDescription: `<p>${product.subtitle ?? product.title}</p>` },
        ru: {},
        hy: {},
      },
      seo: {
        metaTitle: product.title,
        metaDescription: product.subtitle ?? product.title,
        keywords: (product.tags ?? []).join(', '),
      },
      featured: (product.tags ?? []).includes('featured'),
      recommended: (product.tags ?? []).includes('recommended'),
      isNew: (product.badges ?? []).includes('new'),
      bestseller: (product.tags ?? []).includes('bestseller'),
      badges: product.badges ?? [],
      reviews: [],
      questions: [],
      createdAt: new Date().toISOString(),
      updatedAt: new Date().toISOString(),
    };
  }

  private compare(left: AdminProduct, right: AdminProduct, sort: AdminProductListFilters['sort']): number {
    switch (sort) {
      case 'price': return left.price - right.price;
      case 'priority': return left.priority - right.priority;
      case 'stock': return left.quantity - right.quantity;
      case 'updated': return right.updatedAt.localeCompare(left.updatedAt);
      case 'title':
      default:
        return left.name.localeCompare(right.name);
    }
  }
}
