import { prisma } from '../../config/database';
import { getStripe } from '../../services/stripe.client';
import { env } from '../../config/env';
import type { Prisma, OrderStatus, ProductType } from '@prisma/client';
import { logger } from '../../utils/logger';

/** Resolve media IDs to public-facing URLs on a product */
function resolveMediaUrls<T extends { imageUrl: string | null; photoId: number | null; videoId: number | null; galleryPhotoIds: unknown }>(product: T) {
  const photoIds = Array.isArray(product.galleryPhotoIds) ? product.galleryPhotoIds as number[] : null;

  return {
    ...product,
    // Prefer gallery photo over external URL
    resolvedImageUrl: product.photoId
      ? `/media/public/photos/${product.photoId}/image?size=medium`
      : product.imageUrl ?? null,
    thumbnailUrl: product.photoId
      ? `/media/public/photos/${product.photoId}/thumbnail`
      : null,
    // Promotional video
    videoThumbnailUrl: product.videoId
      ? `/media/videos/${product.videoId}/thumbnail`
      : null,
    videoStreamUrl: product.videoId
      ? `/media/videos/${product.videoId}/stream`
      : null,
    // Gallery photo array
    galleryImages: photoIds
      ? photoIds.map((id: number) => ({
          photoId: id,
          thumbnailUrl: `/media/public/photos/${id}/thumbnail`,
          imageUrl: `/media/public/photos/${id}/image?size=medium`,
        }))
      : null,
  };
}

/** Map product type to gallery ad defaults */
function productAdDefaults(product: { title: string; description: string | null; type: ProductType; slug: string; imageUrl: string | null; photoId: number | null; priceCAD: number }) {
  const priceStr = `$${(product.priceCAD / 100).toFixed(2)}`;
  // Prefer gallery photo URL for ad image
  const imagePath = product.photoId
    ? `/media/public/photos/${product.photoId}/image?size=medium`
    : product.imageUrl ?? null;
  const base = {
    title: product.title,
    subtitle: product.description
      ? product.description.slice(0, 120) + (product.description.length > 120 ? '...' : '')
      : null,
    imagePath,
    variant: 'standard',
    visibility: 'everyone',
    isActive: false, // admin enables manually
    isSystemAd: false,
    frequency: 12,
    position: 10,
  };

  switch (product.type) {
    case 'DONATION':
      return {
        ...base,
        type: 'payment_donate',
        linkUrl: '/donate',
        ctaText: 'Donate Now',
        ctaStyle: 'primary',
        iconEmoji: null,
      };
    case 'EVENT':
      return {
        ...base,
        type: 'payment_shop',
        linkUrl: `/shop/${product.slug}`,
        ctaText: `Get Tickets \u2022 ${priceStr}`,
        ctaStyle: 'primary',
        iconEmoji: null,
      };
    default: // DIGITAL
      return {
        ...base,
        type: 'payment_shop',
        linkUrl: `/shop/${product.slug}`,
        ctaText: `Buy Now \u2022 ${priceStr}`,
        ctaStyle: 'primary',
        iconEmoji: null,
      };
  }
}

export const productsService = {
  /** List active products (public, paginated) */
  async listActive(type?: string, page: number = 1, limit: number = 20) {
    const where: Prisma.ProductWhereInput = { isActive: true };
    if (type) where.type = type as Prisma.EnumProductTypeFilter['equals'];
    const skip = (page - 1) * limit;

    const [products, total] = await Promise.all([
      prisma.product.findMany({
        where,
        orderBy: { createdAt: 'desc' },
        skip,
        take: limit,
      }),
      prisma.product.count({ where }),
    ]);

    return {
      products: products.map(resolveMediaUrls),
      pagination: { page, limit, total, totalPages: Math.ceil(total / limit) },
    };
  },

  /** List all products (admin) */
  async listAll(filters: { page: number; limit: number; type?: string; search?: string }) {
    const { page, limit, type, search } = filters;
    const where: Prisma.ProductWhereInput = {};
    if (type) where.type = type as Prisma.EnumProductTypeFilter['equals'];
    if (search) {
      where.OR = [
        { title: { contains: search, mode: 'insensitive' } },
        { slug: { contains: search, mode: 'insensitive' } },
      ];
    }

    const [products, total] = await Promise.all([
      prisma.product.findMany({
        where,
        skip: (page - 1) * limit,
        take: limit,
        orderBy: { createdAt: 'desc' },
      }),
      prisma.product.count({ where }),
    ]);

    return {
      products: products.map(resolveMediaUrls),
      pagination: { page, limit, total, totalPages: Math.ceil(total / limit) },
    };
  },

  async getById(id: string) {
    const product = await prisma.product.findUnique({ where: { id } });
    return product ? resolveMediaUrls(product) : null;
  },

  /** Get a single active product by slug (public detail page) */
  async getBySlug(slug: string) {
    const product = await prisma.product.findUnique({ where: { slug } });
    if (!product || !product.isActive) return null;
    return resolveMediaUrls(product);
  },

  async create(data: Prisma.ProductUncheckedCreateInput) {
    const product = await prisma.product.create({ data });

    // Auto-create a linked gallery ad only if the feature is enabled
    try {
      const settings = await prisma.siteSettings.findFirst();
      if (settings?.enableGalleryAds) {
        const adData = productAdDefaults(product);
        await prisma.ad.create({
          data: { ...adData, productId: product.id },
        });
        logger.info(`Auto-created gallery ad for product "${product.title}" (${product.id})`);
      }
    } catch (err) {
      // Non-critical — log but don't fail the product creation
      logger.warn(`Failed to auto-create gallery ad for product ${product.id}: ${err}`);
    }

    return resolveMediaUrls(product);
  },

  async update(id: string, data: Prisma.ProductUncheckedUpdateInput) {
    const product = await prisma.product.update({ where: { id }, data });

    // Sync linked gallery ad with updated product info (race-safe: updateMany is a no-op if no ad exists)
    try {
      const updates: Prisma.AdUncheckedUpdateManyInput = { updatedAt: new Date() };
      if (data.title !== undefined) updates.title = data.title;
      if (data.description !== undefined) {
        const desc = typeof data.description === 'string' ? data.description : null;
        updates.subtitle = desc ? desc.slice(0, 120) + (desc.length > 120 ? '...' : '') : null;
      }
      // Prefer photoId URL for ad image, fall back to imageUrl
      if (data.photoId !== undefined || data.imageUrl !== undefined) {
        const pid = data.photoId !== undefined ? data.photoId as number | null : product.photoId;
        updates.imagePath = pid
          ? `/media/public/photos/${pid}/image?size=medium`
          : (data.imageUrl !== undefined ? data.imageUrl as string | null : product.imageUrl);
      }
      if (data.isActive === false) updates.isActive = false;
      if (data.slug !== undefined) {
        // Update link URL for non-donation products
        updates.linkUrl = `/shop/${data.slug}`;
      }
      if (data.priceCAD !== undefined) {
        const price = typeof data.priceCAD === 'number' ? data.priceCAD : product.priceCAD;
        const priceStr = `$${(price / 100).toFixed(2)}`;
        const verb = product.type === 'EVENT' ? 'Get Tickets' : 'Buy Now';
        updates.ctaText = `${verb} \u2022 ${priceStr}`;
      }

      // updateMany with productId filter: no-op if no linked ad exists (no error thrown)
      // Avoids race condition of find-then-update pattern
      await prisma.ad.updateMany({
        where: { productId: id },
        data: updates,
      });
    } catch (err) {
      logger.warn(`Failed to sync gallery ad for product ${id}: ${err}`);
    }

    return resolveMediaUrls(product);
  },

  async delete(id: string) {
    // Deactivate linked gallery ad
    try {
      const linkedAd = await prisma.ad.findUnique({ where: { productId: id } });
      if (linkedAd) {
        await prisma.ad.update({
          where: { productId: id },
          data: { isActive: false, updatedAt: new Date() },
        });
      }
    } catch (err) {
      logger.warn(`Failed to deactivate gallery ad for product ${id}: ${err}`);
    }

    const orders = await prisma.order.count({ where: { productId: id, status: 'COMPLETED' } });
    if (orders > 0) {
      // Soft delete by deactivating
      return prisma.product.update({ where: { id }, data: { isActive: false } });
    }
    return prisma.product.delete({ where: { id } });
  },

  /** Create Stripe Checkout for a product purchase */
  async createProductCheckout(productId: string, buyerEmail: string, buyerName?: string, userId?: string) {
    const stripe = await getStripe();

    // Atomic availability check to prevent overselling under concurrency
    const product = await prisma.$transaction(async (tx) => {
      const p = await tx.product.findUnique({ where: { id: productId } });
      if (!p || !p.isActive) throw new Error('Product not found or inactive');
      if (p.maxPurchases && p.purchaseCount >= p.maxPurchases) {
        throw new Error('Product is sold out');
      }
      return p;
    });

    const session = await stripe.checkout.sessions.create({
      mode: 'payment',
      line_items: [{
        price_data: {
          currency: 'cad',
          product_data: {
            name: product.title,
            description: product.description || undefined,
          },
          unit_amount: product.priceCAD,
        },
        quantity: 1,
      }],
      customer_email: buyerEmail,
      success_url: `${env.ADMIN_URL}/payments/success?session_id={CHECKOUT_SESSION_ID}`,
      cancel_url: `${env.ADMIN_URL}/shop`,
      metadata: {
        type: 'product',
        productId: product.id,
        userId: userId || '',
        buyerEmail,
        buyerName: buyerName || '',
      },
    });

    // Create pending order
    await prisma.order.create({
      data: {
        userId: userId || null,
        productId: product.id,
        amountCAD: product.priceCAD,
        status: 'PENDING',
        stripeCheckoutSessionId: session.id,
        type: 'product',
        buyerEmail,
        buyerName: buyerName || null,
      },
    });

    return { sessionId: session.id, url: session.url };
  },

  /** Sync product to Stripe */
  async syncProductToStripe(id: string) {
    const stripe = await getStripe();
    const product = await prisma.product.findUnique({ where: { id } });
    if (!product) throw new Error('Product not found');

    let stripeProductId = product.stripeProductId;

    if (stripeProductId) {
      await stripe.products.update(stripeProductId, {
        name: product.title,
        description: product.description || undefined,
        active: product.isActive,
      });
    } else {
      const sp = await stripe.products.create({
        name: product.title,
        description: product.description || undefined,
        active: product.isActive,
        metadata: { productId: product.id },
      });
      stripeProductId = sp.id;
    }

    let stripePriceId = product.stripePriceId;
    if (!stripePriceId) {
      const price = await stripe.prices.create({
        product: stripeProductId,
        unit_amount: product.priceCAD,
        currency: 'cad',
      });
      stripePriceId = price.id;
    }

    return prisma.product.update({
      where: { id },
      data: { stripeProductId, stripePriceId },
    });
  },

  /** List orders (admin) */
  async listOrders(filters: {
    page: number;
    limit: number;
    status?: OrderStatus;
    type?: string;
    search?: string;
  }) {
    const { page, limit, status, type, search } = filters;
    const where: Prisma.OrderWhereInput = {};
    if (status) where.status = status;
    if (type) where.type = type;
    if (search) {
      where.OR = [
        { buyerEmail: { contains: search, mode: 'insensitive' } },
        { buyerName: { contains: search, mode: 'insensitive' } },
      ];
    }

    const [orders, total] = await Promise.all([
      prisma.order.findMany({
        where,
        include: {
          product: { select: { id: true, title: true, slug: true, type: true } },
          user: { select: { id: true, email: true, name: true } },
        },
        skip: (page - 1) * limit,
        take: limit,
        orderBy: { createdAt: 'desc' },
      }),
      prisma.order.count({ where }),
    ]);

    return {
      orders,
      pagination: { page, limit, total, totalPages: Math.ceil(total / limit) },
    };
  },

  /** Refund an order via Stripe */
  async refundOrder(orderId: string) {
    const order = await prisma.order.findUnique({ where: { id: orderId } });
    if (!order) throw new Error('Order not found');
    if (order.status !== 'COMPLETED') throw new Error('Can only refund completed orders');

    if (order.stripePaymentIntentId) {
      const stripe = await getStripe();
      await stripe.refunds.create({ payment_intent: order.stripePaymentIntentId });
    }

    // Stripe refund succeeded — update DB. If this fails, the charge.refunded
    // webhook will reconcile the status as a fallback.
    try {
      return await prisma.order.update({
        where: { id: orderId },
        data: { status: 'REFUNDED' },
      });
    } catch (dbErr) {
      logger.error(`Stripe refund succeeded but DB update failed for order ${orderId}. Webhook will reconcile.`, dbErr);
      throw new Error('Refund processed by Stripe but local status update failed. It will be reconciled shortly.');
    }
  },
};
