import { Injectable, computed, inject, signal } from '@angular/core';
import { forkJoin, Subject } from 'rxjs';
import { take, takeUntil } from 'rxjs/operators';
import {
  AdminAnalyticsDateRange,
  AdminAnalyticsSeriesPoint,
  AdminAnalyticsSummary,
  AdminAnalyticsTopProduct,
  AdminCustomerAnalytics,
  AdminLowStockProduct,
  AdminMarketplaceHealthCheck,
  AdminProductAnalytics,
  AdminRecentActivityEntry,
  AdminRecommendationCard,
} from '../models/admin-analytics.model';
import { ADMIN_ORDERS_GATEWAY } from '../../orders/services/admin-orders-gateway.token';
import { ADMIN_PRODUCTS_GATEWAY } from '../../products/services/admin-products-gateway.token';
import { ADMIN_CATEGORIES_GATEWAY } from '../../categories/services/admin-categories-gateway.token';
import { ADMIN_MODERATION_GATEWAY } from '../../moderation/services/admin-moderation-gateway.token';
import { AdminDashboardFacade } from '../../dashboard/facade/admin-dashboard.facade';
import { AdminOrder } from '../../orders/models/admin-order.model';
import { AdminProduct } from '../../products/models/admin-product.model';
import { AdminCategory } from '../../categories/models/admin-category.model';
import { AdminReview } from '../../moderation/models/admin-review.model';

/**
 * Aggregates existing gateways (orders/products/categories/reviews) plus the dashboard facade's
 * bootstrap/validation state into merchant-facing analytics. Never fabricates a number: anything
 * with no real data source (visitor traffic, search queries, product views) stays null/'unknown'
 * and is rendered honestly rather than guessed, per Sprint 12 scope.
 */
@Injectable({ providedIn: 'root' })
export class AdminAnalyticsFacade {
  private readonly ordersGateway = inject(ADMIN_ORDERS_GATEWAY);
  private readonly productsGateway = inject(ADMIN_PRODUCTS_GATEWAY);
  private readonly categoriesGateway = inject(ADMIN_CATEGORIES_GATEWAY);
  private readonly moderationGateway = inject(ADMIN_MODERATION_GATEWAY);
  private readonly dashboardFacade = inject(AdminDashboardFacade);

  readonly dateRange = signal<AdminAnalyticsDateRange>(30);
  readonly loading = signal(false);
  readonly error = signal(false);
  readonly summary = signal<AdminAnalyticsSummary | null>(null);
  readonly salesSeries = signal<AdminAnalyticsSeriesPoint[]>([]);
  readonly topProducts = signal<AdminAnalyticsTopProduct[]>([]);
  readonly lowStockProducts = signal<AdminLowStockProduct[]>([]);
  readonly recentActivity = signal<AdminRecentActivityEntry[]>([]);
  readonly marketplaceHealth = signal<AdminMarketplaceHealthCheck[]>([]);
  readonly productAnalytics = signal<AdminProductAnalytics | null>(null);
  readonly customerAnalytics = signal<AdminCustomerAnalytics | null>(null);
  readonly recommendations = signal<AdminRecommendationCard[]>([]);

  readonly maxSeriesValue = computed(() => Math.max(1, ...this.salesSeries().map(point => point.value)));

  readonly healthCompletionPercent = computed(() => {
    const known = this.marketplaceHealth().filter(check => check.status !== 'unknown');
    if (known.length === 0) {
      return 0;
    }
    const healthy = known.filter(check => check.status === 'healthy').length;
    return Math.round((healthy / known.length) * 100);
  });

  readonly warnings = computed(() => this.recommendations().filter(card => card.severity !== 'info'));

  private readonly cancelPreviousLoad$ = new Subject<void>();

  load(): void {
    // Cancel any still-in-flight previous load so a rapid setDateRange() double-call
    // can't have a stale response overwrite a newer one.
    this.cancelPreviousLoad$.next();

    this.loading.set(true);
    this.error.set(false);
    this.dashboardFacade.ensureLoaded();
    this.recentActivity.set(
      this.dashboardFacade.activityEntries().map(entry => ({
        id: entry.id,
        labelKey: entry.type === 'published' ? 'adminAnalytics.activityPublished' : 'adminAnalytics.activityDraftSaved',
        timestamp: entry.timestamp,
      })),
    );

    forkJoin({
      orderResult: this.ordersGateway.loadOrders({ search: '', status: 'all', page: 1, pageSize: 100000 }),
      productResult: this.productsGateway.loadProducts({ search: '', categoryId: null, visibility: 'all', stock: 'all', includeArchived: true, sort: 'title', page: 1, pageSize: 100000 }),
      categories: this.categoriesGateway.loadCategories({ search: '', visibility: 'all', includeDeleted: true }),
      reviewResult: this.moderationGateway.loadReviews({ search: '', status: 'all', rating: 'all', page: 1, pageSize: 100000 }),
    }).pipe(take(1), takeUntil(this.cancelPreviousLoad$)).subscribe({
      next: ({ orderResult, productResult, categories, reviewResult }) => {
        const cutoff = Date.now() - this.dateRange() * 24 * 60 * 60 * 1000;
        const inRange = orderResult.items.filter(order => new Date(order.createdAt).getTime() >= cutoff);

        this.salesSeries.set(this.buildSeries(inRange, this.dateRange()));
        this.topProducts.set(this.buildTopProducts(inRange));
        this.customerAnalytics.set(this.buildCustomerAnalytics(orderResult.items, inRange, this.dateRange()));

        const revenueTotal = inRange.reduce((sum, order) => sum + order.total, 0);
        const ordersCount = inRange.length;
        const uniqueCustomers = new Set(inRange.map(order => order.customer.email)).size;

        const products = productResult.items;
        const reviews = reviewResult.items;

        this.summary.set({
          revenueTotal,
          currency: inRange[0]?.currency ?? 'RUB',
          ordersCount,
          avgOrderValue: ordersCount > 0 ? Math.round(revenueTotal / ordersCount) : 0,
          productsCount: products.length,
          categoriesCount: categories.length,
          customersCount: uniqueCustomers,
          conversionRate: null,
        });

        this.lowStockProducts.set(this.buildLowStock(products));
        this.productAnalytics.set(this.buildProductAnalytics(products));
        this.marketplaceHealth.set(this.buildMarketplaceHealth(products, categories, reviews, orderResult.items));
        this.recommendations.set(this.buildRecommendations(products, categories));

        this.loading.set(false);
      },
      error: () => { this.loading.set(false); this.error.set(true); }
    });
  }

  setDateRange(range: AdminAnalyticsDateRange): void {
    this.dateRange.set(range);
    this.load();
  }

  exportCsv(): string {
    const header = 'Date,Revenue';
    const rows = this.salesSeries().map(point => `${point.date},${point.value}`);
    return [header, ...rows].join('\n');
  }

  exportTopProductsCsv(): string {
    const header = 'Product,Quantity Sold,Revenue';
    const rows = this.topProducts().map(product => `${product.name},${product.quantity},${product.revenue}`);
    return [header, ...rows].join('\n');
  }

  exportHealthCsv(): string {
    const header = 'Check,Status,Value';
    const rows = this.marketplaceHealth().map(check => `${check.labelKey},${check.status},${check.displayValue ?? ''}`);
    return [header, ...rows].join('\n');
  }

  private buildSeries(orders: AdminOrder[], days: number): AdminAnalyticsSeriesPoint[] {
    const buckets = new Map<string, number>();
    for (let i = days - 1; i >= 0; i--) {
      const date = new Date(Date.now() - i * 24 * 60 * 60 * 1000).toISOString().slice(0, 10);
      buckets.set(date, 0);
    }
    for (const order of orders) {
      const date = order.createdAt.slice(0, 10);
      if (buckets.has(date)) {
        buckets.set(date, (buckets.get(date) ?? 0) + order.total);
      }
    }
    return [...buckets.entries()].map(([date, value]) => ({ date, value }));
  }

  private buildTopProducts(orders: AdminOrder[]): AdminAnalyticsTopProduct[] {
    const map = new Map<string, AdminAnalyticsTopProduct>();
    for (const order of orders) {
      for (const item of order.items) {
        const existing = map.get(item.productId) ?? { productId: item.productId, name: item.name, quantity: 0, revenue: 0 };
        existing.quantity += item.quantity;
        existing.revenue += item.price * item.quantity;
        map.set(item.productId, existing);
      }
    }
    return [...map.values()].sort((left, right) => right.revenue - left.revenue).slice(0, 5);
  }

  private buildLowStock(products: AdminProduct[]): AdminLowStockProduct[] {
    return products
      .filter(product => !product.archived && (product.stockStatus === 'low_stock' || product.stockStatus === 'out_of_stock'))
      .sort((left, right) => left.quantity - right.quantity)
      .slice(0, 8)
      .map(product => ({ productId: product.id, name: product.name, quantity: product.quantity, stockStatus: product.stockStatus as 'low_stock' | 'out_of_stock' }));
  }

  private buildCustomerAnalytics(allOrders: AdminOrder[], inRangeOrders: AdminOrder[], days: number): AdminCustomerAnalytics {
    const firstOrderByEmail = new Map<string, number>();
    for (const order of allOrders) {
      const email = order.customer.email;
      const time = new Date(order.createdAt).getTime();
      const existing = firstOrderByEmail.get(email);
      if (existing === undefined || time < existing) {
        firstOrderByEmail.set(email, time);
      }
    }

    const cutoff = Date.now() - days * 24 * 60 * 60 * 1000;
    const emailsInRange = new Set(inRangeOrders.map(order => order.customer.email));
    let newCustomers = 0;
    let returningCustomers = 0;
    for (const email of emailsInRange) {
      const firstOrderAt = firstOrderByEmail.get(email) ?? Date.now();
      if (firstOrderAt >= cutoff) {
        newCustomers += 1;
      } else {
        returningCustomers += 1;
      }
    }

    const revenueInRange = inRangeOrders.reduce((sum, order) => sum + order.total, 0);
    const uniqueCustomersInRange = emailsInRange.size;

    return {
      newCustomers,
      returningCustomers,
      averageSpend: uniqueCustomersInRange > 0 ? Math.round(revenueInRange / uniqueCustomersInRange) : 0,
      currency: inRangeOrders[0]?.currency ?? 'RUB',
      retentionPercent: uniqueCustomersInRange > 0 ? Math.round((returningCustomers / uniqueCustomersInRange) * 100) : null,
    };
  }

  private buildProductAnalytics(products: AdminProduct[]): AdminProductAnalytics {
    const mostReviewed = [...products]
      .filter(product => product.reviews.length > 0)
      .sort((left, right) => right.reviews.length - left.reviews.length)
      .slice(0, 5)
      .map(product => ({ productId: product.id, name: product.name, value: product.reviews.length }));

    const worstRated = [...products]
      .filter(product => product.reviews.length > 0)
      .map(product => ({
        productId: product.id,
        name: product.name,
        value: Math.round((product.reviews.reduce((sum, review) => sum + review.rating, 0) / product.reviews.length) * 10) / 10,
      }))
      .sort((left, right) => left.value - right.value)
      .slice(0, 5);

    return {
      topSelling: this.topProducts(),
      mostReviewed,
      worstRated,
      hiddenCount: products.filter(product => !product.visible && !product.archived).length,
      archivedCount: products.filter(product => product.archived).length,
    };
  }

  private buildMarketplaceHealth(products: AdminProduct[], categories: AdminCategory[], reviews: AdminReview[], allOrders: AdminOrder[]): AdminMarketplaceHealthCheck[] {
    const activeProducts = products.filter(product => !product.archived);
    const withImages = activeProducts.filter(product => product.media.images.length > 0).length;
    const withSeo = activeProducts.filter(product => product.seo.metaTitle && product.seo.metaDescription).length;
    const categoriesWithProducts = categories.filter(category => category.itemsCount > 0).length;
    const moderatedReviews = reviews.filter(review => review.status !== 'pending').length;
    const processedOrders = allOrders.filter(order => order.status !== 'pending').length;
    const bootstrap = this.dashboardFacade.bootstrap();
    const issues = new Set(this.dashboardFacade.validationIssues().map(issue => issue.code));

    const percentStatus = (done: number, total: number) => {
      if (total === 0) return 'unknown' as const;
      const ratio = done / total;
      if (ratio === 1) return 'healthy' as const;
      if (ratio >= 0.5) return 'attention' as const;
      return 'unhealthy' as const;
    };

    return [
      {
        code: 'products-with-images', labelKey: 'adminMarketplaceHealth.checkImages',
        status: percentStatus(withImages, activeProducts.length),
        displayValue: activeProducts.length ? `${withImages}/${activeProducts.length}` : null,
        actionRoute: ['backoffice', 'products'],
      },
      {
        code: 'products-with-seo', labelKey: 'adminMarketplaceHealth.checkSeo',
        status: percentStatus(withSeo, activeProducts.length),
        displayValue: activeProducts.length ? `${withSeo}/${activeProducts.length}` : null,
        actionRoute: ['backoffice', 'products'],
      },
      {
        code: 'categories-with-products', labelKey: 'adminMarketplaceHealth.checkCategories',
        status: percentStatus(categoriesWithProducts, categories.length),
        displayValue: categories.length ? `${categoriesWithProducts}/${categories.length}` : null,
        actionRoute: ['backoffice', 'categories'],
      },
      {
        code: 'reviews-moderated', labelKey: 'adminMarketplaceHealth.checkReviews',
        status: percentStatus(moderatedReviews, reviews.length),
        displayValue: reviews.length ? `${moderatedReviews}/${reviews.length}` : null,
        actionRoute: ['backoffice', 'moderation'],
      },
      {
        code: 'orders-processed', labelKey: 'adminMarketplaceHealth.checkOrders',
        status: percentStatus(processedOrders, allOrders.length),
        displayValue: allOrders.length ? `${processedOrders}/${allOrders.length}` : null,
        actionRoute: ['backoffice', 'orders'],
      },
      {
        code: 'translations-complete', labelKey: 'adminMarketplaceHealth.checkTranslations',
        status: !bootstrap ? 'unknown' : issues.has('missing-translations') ? 'unhealthy' : 'healthy',
        actionRoute: ['edit'],
      },
      {
        code: 'homepage-configured', labelKey: 'adminMarketplaceHealth.checkHomepage',
        status: !bootstrap ? 'unknown' : this.dashboardFacade.enabledWidgetsCount() > 0 ? 'healthy' : 'attention',
        actionRoute: ['edit', 'homepage'],
      },
      {
        code: 'backend-connectivity', labelKey: 'adminMarketplaceHealth.checkBackend',
        status: bootstrap ? 'healthy' : 'unknown',
        actionRoute: ['backoffice', 'monitoring'],
      },
      {
        code: 'performance', labelKey: 'adminMarketplaceHealth.checkPerformance',
        status: 'unknown',
        actionRoute: ['backoffice', 'monitoring'],
      },
    ];
  }

  private buildRecommendations(products: AdminProduct[], categories: AdminCategory[]): AdminRecommendationCard[] {
    const cards: AdminRecommendationCard[] = [];
    const activeProducts = products.filter(product => !product.archived);
    const missingImages = activeProducts.filter(product => product.media.images.length === 0).length;
    const missingSeo = activeProducts.filter(product => !product.seo.metaTitle || !product.seo.metaDescription).length;
    const missingCategory = activeProducts.filter(product => !product.categoryId).length;
    const emptyCategories = categories.filter(category => category.itemsCount === 0).length;

    if (missingImages > 0) {
      cards.push({ id: 'missing-images', labelKey: 'adminMarketplaceHealth.recommendImages', descriptionKey: 'adminMarketplaceHealth.recommendImagesCount', severity: 'warning', route: ['backoffice', 'products'] });
    }
    if (missingSeo > 0) {
      cards.push({ id: 'missing-seo', labelKey: 'adminMarketplaceHealth.recommendSeo', descriptionKey: 'adminMarketplaceHealth.recommendSeoCount', severity: 'warning', route: ['backoffice', 'products'] });
    }
    if (missingCategory > 0) {
      cards.push({ id: 'missing-category', labelKey: 'adminMarketplaceHealth.recommendCategory', descriptionKey: 'adminMarketplaceHealth.recommendCategoryCount', severity: 'warning', route: ['backoffice', 'products'] });
    }
    if (emptyCategories > 0) {
      cards.push({ id: 'empty-categories', labelKey: 'adminMarketplaceHealth.recommendEmptyCategories', descriptionKey: 'adminMarketplaceHealth.recommendEmptyCategoriesCount', severity: 'info', route: ['backoffice', 'categories'] });
    }
    if (this.dashboardFacade.bootstrap() && this.dashboardFacade.enabledWidgetsCount() === 0) {
      cards.push({ id: 'homepage-incomplete', labelKey: 'adminMarketplaceHealth.recommendHomepage', severity: 'critical', route: ['edit', 'homepage'] });
    }
    if (this.dashboardFacade.bootstrap() && this.dashboardFacade.staticPagesUnpublishedCount() > 0) {
      cards.push({ id: 'static-pages-unpublished', labelKey: 'adminMarketplaceHealth.recommendStaticPages', severity: 'info', route: ['backoffice', 'static-pages'] });
    }

    return cards;
  }
}
