import { Injectable, Logger } from '@nestjs/common';
import { PrismaService } from '../../../prisma/prisma.service';
import { ConnectorFactory } from '../../connectors/connector.factory';
import { BusinessService } from '../../business/business.service';

@Injectable()
export class SyncQueueService {
  private readonly logger = new Logger(SyncQueueService.name);

  constructor(
    private readonly prisma: PrismaService,
    private readonly connectorFactory: ConnectorFactory,
    private readonly businessService: BusinessService,
  ) {}

  async triggerSync(businessId: string, syncType: 'products' | 'categories' = 'products'): Promise<string> {
    // 1. Fetch active integration
    const integration = await this.prisma.businessIntegration.findFirst({
      where: { businessId, integrationType: 'woocommerce', status: 'ACTIVE' },
    });

    if (!integration) {
      throw new Error('No active integration found for this business.');
    }

    // 2. Create running sync log
    const syncLog = await this.prisma.syncLog.create({
      data: {
        businessId,
        integrationId: integration.id,
        syncType,
        status: 'RUNNING',
        startedAt: new Date(),
      },
    });

    // 3. Trigger sync process asynchronously (non-blocking)
    this.runBackgroundSync(businessId, integration, syncLog.id, syncType).catch((err) => {
      this.logger.error(`Sync job ${syncLog.id} failed: ${err.message}`, err.stack);
    });

    return syncLog.id;
  }

  private async runBackgroundSync(
    businessId: string,
    integration: any,
    syncLogId: string,
    syncType: string,
  ) {
    let recordsProcessed = 0;
    let recordsFailed = 0;

    try {
      // Fetch decrypted credentials
      const credentials = await this.businessService.getDecryptedCredentials(businessId, integration.integrationType);
      if (!credentials) {
        throw new Error('Could not decrypt integration credentials');
      }

      // Instantiate connector
      const connector = this.connectorFactory.getConnector(integration.integrationType, credentials);

      if (syncType === 'categories' || syncType === 'products') {
        // Sync Categories First (or solely if syncType === 'categories')
        const categories = await connector.fetchCategories();
        for (const cat of categories) {
          await this.prisma.category.upsert({
            where: {
              businessId_sourceSystem_externalCategoryId: {
                businessId,
                sourceSystem: integration.integrationType,
                externalCategoryId: cat.externalId,
              },
            },
            update: {
              name: cat.name,
              parentId: cat.parentId,
            },
            create: {
              businessId,
              sourceSystem: integration.integrationType,
              externalCategoryId: cat.externalId,
              name: cat.name,
              parentId: cat.parentId,
            },
          });
          if (syncType === 'categories') {
            recordsProcessed++;
          }
        }
      }

      if (syncType === 'products') {
        // Sync Products
        const products = await connector.fetchProducts();
        for (const prod of products) {
          try {
            // Check for existing product by composite key first
            let dbProduct = await this.prisma.product.findUnique({
              where: {
                businessId_sourceSystem_externalProductId: {
                  businessId,
                  sourceSystem: integration.integrationType,
                  externalProductId: prod.externalId,
                },
              },
            });

            // If not found, deduplicate by exact SKU (e.g. products created via Admin Dashboard)
            if (!dbProduct && prod.sku && prod.sku.trim()) {
              dbProduct = await this.prisma.product.findFirst({
                where: { businessId, sku: prod.sku.trim(), status: { not: 'INACTIVE' } },
              });
            }

            // If still not found, deduplicate by exact Name
            if (!dbProduct && prod.name && prod.name.trim()) {
              dbProduct = await this.prisma.product.findFirst({
                where: { businessId, name: prod.name.trim(), status: { not: 'INACTIVE' } },
              });
            }

            if (dbProduct) {
              // Update existing product and link externalProductId smoothly without duplicating
              dbProduct = await this.prisma.product.update({
                where: { id: dbProduct.id },
                data: {
                  externalProductId: prod.externalId,
                  name: prod.name || dbProduct.name,
                  description: prod.description || dbProduct.description,
                  sku: prod.sku || dbProduct.sku,
                  price: prod.price,
                  currency: prod.currency,
                  stockQuantity: prod.stockQuantity,
                  stockStatus: prod.stockStatus,
                  imageUrl: prod.imageUrl || dbProduct.imageUrl,
                  lastSyncedAt: new Date(),
                },
              });
            } else {
              // Create brand new product
              dbProduct = await this.prisma.product.create({
                data: {
                  businessId,
                  sourceSystem: integration.integrationType,
                  externalProductId: prod.externalId,
                  name: prod.name,
                  description: prod.description,
                  sku: prod.sku,
                  price: prod.price,
                  currency: prod.currency,
                  stockQuantity: prod.stockQuantity,
                  stockStatus: prod.stockStatus,
                  imageUrl: prod.imageUrl,
                  lastSyncedAt: new Date(),
                },
              });
            }

            // Sync Product Variants if any
            if (prod.variants && prod.variants.length > 0) {
              for (const v of prod.variants) {
                await this.prisma.productVariant.upsert({
                  where: {
                    productId_externalVariantId: {
                      productId: dbProduct.id,
                      externalVariantId: v.externalId,
                    },
                  },
                  update: {
                    name: v.name,
                    sku: v.sku,
                    price: v.price,
                    stockQuantity: v.stockQuantity,
                    stockStatus: v.stockStatus,
                    attributesJson: v.attributes ? JSON.stringify(v.attributes) : null,
                  },
                  create: {
                    productId: dbProduct.id,
                    externalVariantId: v.externalId,
                    name: v.name,
                    sku: v.sku,
                    price: v.price,
                    stockQuantity: v.stockQuantity,
                    stockStatus: v.stockStatus,
                    attributesJson: v.attributes ? JSON.stringify(v.attributes) : null,
                  },
                });
              }
            }

            recordsProcessed++;
          } catch (pErr) {
            this.logger.error(`Failed to sync individual product ${prod.name}: ${pErr.message}`);
            recordsFailed++;
          }
        }
      }

      // Update sync log to COMPLETED
      await this.prisma.syncLog.update({
        where: { id: syncLogId },
        data: {
          status: 'COMPLETED',
          recordsProcessed,
          recordsFailed,
          completedAt: new Date(),
        },
      });

      // Update last connected timestamp on integration
      await this.businessService.updateLastConnected(integration.id);
    } catch (err: any) {
      // Update sync log to FAILED
      await this.prisma.syncLog.update({
        where: { id: syncLogId },
        data: {
          status: 'FAILED',
          errorMessage: err.message,
          recordsProcessed,
          recordsFailed,
          completedAt: new Date(),
        },
      });
    }
  }

  async pushProductChange(businessId: string, productId: string, action: 'CREATE' | 'UPDATE' | 'DELETE', cachedProduct?: any, targetPlatforms?: string): Promise<void> {
    try {
      const integrations = await this.prisma.businessIntegration.findMany({
        where: { businessId, status: 'ACTIVE' },
      });

      if (integrations.length === 0) return;

      const product = await this.prisma.product.findUnique({
        where: { id: productId },
      });

      if (!product && action !== 'DELETE') return;

      for (const integration of integrations) {
        if (targetPlatforms && targetPlatforms !== 'all' && targetPlatforms !== 'both') {
          if (integration.integrationType !== targetPlatforms) {
            continue; // Skip because user selected WooCommerce Only or Meta Catalog Only!
          }
        } else if (!targetPlatforms && product && product.sourceSystem && product.sourceSystem !== 'manual' && product.sourceSystem !== 'all') {
          if (integration.integrationType !== product.sourceSystem) {
            continue; // Respect saved target platform when syncing updates/deletes!
          }
        }

        this.runOutboundProductSync(businessId, integration, product || cachedProduct || { id: productId, externalProductId: productId }, action).catch((err) => {
          this.logger.error(`Outbound sync error (${action}) for integration ${integration.id}: ${err.message}`);
        });
      }
    } catch (err: any) {
      this.logger.error(`Failed to dispatch pushProductChange: ${err.message}`);
    }
  }

  private async runOutboundProductSync(businessId: string, integration: any, product: any, action: 'CREATE' | 'UPDATE' | 'DELETE'): Promise<void> {
    const credentials = await this.businessService.getDecryptedCredentials(businessId, integration.integrationType);
    if (!credentials) return;

    let connector: any;
    try {
      connector = this.connectorFactory.getConnector(integration.integrationType, credentials);
    } catch {
      return; // Skip unsupported integrations gracefully
    }

    const syncLog = await this.prisma.syncLog.create({
      data: {
        businessId,
        integrationId: integration.id,
        syncType: `outbound_${action.toLowerCase()}`,
        status: 'RUNNING',
        startedAt: new Date(),
      },
    });

    try {
      let recordsProcessed = 0;
      const universalProduct = product.name ? {
        externalId: product.externalProductId || product.id,
        name: product.name,
        description: product.description,
        sku: product.sku,
        price: Number(product.price || 0),
        currency: product.currency || 'USD',
        stockQuantity: product.stockQuantity,
        stockStatus: product.stockStatus as any,
        imageUrl: product.imageUrl,
        variants: [],
      } : { externalId: product.externalProductId || product.id, name: '', price: 0, stockStatus: 'instock' } as any;

      if (action === 'CREATE' && connector.createProduct) {
        const result = await connector.createProduct(universalProduct);
        if (result?.externalId && result.externalId !== product.id && product.name) {
          await this.prisma.product.update({
            where: { id: product.id },
            data: { externalProductId: result.externalId },
          });
        }
        recordsProcessed = 1;
      } else if (action === 'UPDATE' && connector.updateProduct) {
        await connector.updateProduct(product.externalProductId || product.id, universalProduct);
        recordsProcessed = 1;
      } else if (action === 'DELETE' && connector.deleteProduct) {
        await connector.deleteProduct(product.externalProductId || product.id, product);
        recordsProcessed = 1;
      }

      await this.prisma.syncLog.update({
        where: { id: syncLog.id },
        data: {
          status: 'COMPLETED',
          recordsProcessed,
          completedAt: new Date(),
        },
      });
      await this.businessService.updateLastConnected(integration.id);
    } catch (err: any) {
      this.logger.error(`Outbound sync (${action}) failed for ${integration.integrationType}: ${err.message}`);
      await this.prisma.syncLog.update({
        where: { id: syncLog.id },
        data: {
          status: 'FAILED',
          errorMessage: err.message,
          completedAt: new Date(),
        },
      });
    }
  }

  async getSyncLogs(businessId: string) {
    return this.prisma.syncLog.findMany({
      where: { businessId },
      orderBy: { startedAt: 'desc' },
      take: 20,
    });
  }
}
