/**
 * Marketplace Repository — CRUD for all 5 marketplace containers.
 * Cloud-agnostic via @bytelyst/datastore.
 */

import type { FilterMap } from '@bytelyst/datastore';
import { getCollection } from '../../lib/datastore.js';
import type {
  MarketplaceListingDoc,
  MarketplaceReviewDoc,
  MarketplaceInstallDoc,
  MarketplaceCertificationDoc,
  MarketplaceReportDoc,
  MarketplaceVoteDoc,
  ListListingsQuery,
  ListReviewsQuery,
  ListInstallsQuery,
  ListReportsQuery,
  ListMyReportsQuery,
} from './types.js';

// ─────────────────────────────────────────────────────────────────────────────
// Listings
// ─────────────────────────────────────────────────────────────────────────────

function listingsCollection() {
  return getCollection<MarketplaceListingDoc>('marketplace_listings', '/id');
}

export async function listListings(
  query: ListListingsQuery
): Promise<{ items: MarketplaceListingDoc[]; total: number }> {
  const filter: FilterMap = {};

  if (query.productId) filter.productId = query.productId;
  if (query.templateType) filter.templateType = query.templateType;
  if (query.category) filter.category = query.category;
  if (query.pricingModel) filter.pricingModel = query.pricingModel;

  // Only show approved, published listings in public queries
  filter.certificationStatus = 'approved';
  filter.visibility = 'public';

  const sortField =
    query.sortBy === 'rating'
      ? 'averageRating'
      : query.sortBy === 'newest'
        ? 'createdAt'
        : query.sortBy;
  const sortDir = query.sortOrder === 'desc' ? -1 : 1;

  let allDocs = await listingsCollection().findMany({
    filter,
    sort: { [sortField]: sortDir },
  });

  // In-memory tag filtering (ARRAY_CONTAINS simulation)
  if (query.tags) {
    const tagList = query.tags.split(',').map(t => t.trim().toLowerCase());
    allDocs = allDocs.filter(d => tagList.some(tag => d.tags?.some(t => t.toLowerCase().includes(tag))));
  }

  // In-memory min rating filter
  if (query.minRating !== undefined) {
    allDocs = allDocs.filter(d => d.averageRating >= query.minRating!);
  }

  // In-memory text search
  if (query.q) {
    const q = query.q.toLowerCase();
    allDocs = allDocs.filter(
      d =>
        d.title?.toLowerCase().includes(q) ||
        d.shortDescription?.toLowerCase().includes(q) ||
        d.description?.toLowerCase().includes(q)
    );
  }

  const total = allDocs.length;
  const items = allDocs.slice(query.offset, query.offset + query.limit);

  return { items, total };
}

export async function listMyListings(
  authorId: string,
  productId?: string
): Promise<MarketplaceListingDoc[]> {
  const filter: FilterMap = { authorId };
  if (productId) filter.productId = productId;

  return listingsCollection().findMany({
    filter,
    sort: { updatedAt: -1 },
  });
}

export async function getListingById(id: string): Promise<MarketplaceListingDoc | null> {
  try {
    return await listingsCollection().findById(id, id);
  } catch {
    return null;
  }
}

export async function createListing(doc: MarketplaceListingDoc): Promise<MarketplaceListingDoc> {
  return listingsCollection().create(doc);
}

export async function updateListing(
  id: string,
  updates: Partial<MarketplaceListingDoc>
): Promise<MarketplaceListingDoc | null> {
  try {
    return await listingsCollection().update(id, id, { ...updates, updatedAt: new Date().toISOString() });
  } catch {
    return null;
  }
}

export async function deleteListing(id: string): Promise<boolean> {
  try {
    await listingsCollection().delete(id, id);
    return true;
  } catch {
    return false;
  }
}

export async function updateListingStats(
  id: string,
  stats: Partial<Pick<MarketplaceListingDoc, 'installCount' | 'reviewCount' | 'averageRating' | 'voteCount'>>
): Promise<void> {
  await updateListing(id, stats);
}

export async function listPendingCertification(productId?: string): Promise<MarketplaceListingDoc[]> {
  const filter: FilterMap = { certificationStatus: 'submitted' };
  if (productId) filter.productId = productId;

  return listingsCollection().findMany({
    filter,
    sort: { updatedAt: 1 },
  });
}

export async function listFeaturedListings(productId: string, limit = 10): Promise<MarketplaceListingDoc[]> {
  const filter: FilterMap = { productId, featured: true, certificationStatus: 'approved', visibility: 'public' };

  const docs = await listingsCollection().findMany({
    filter,
    sort: { installCount: -1 },
    limit,
  });

  return docs;
}

export async function listCategories(productId: string): Promise<string[]> {
  const filter: FilterMap = { productId, certificationStatus: 'approved', visibility: 'public' };

  const docs = await listingsCollection().findMany({ filter });
  const categories = new Set(docs.map(d => d.category));
  return Array.from(categories).sort();
}

// ─────────────────────────────────────────────────────────────────────────────
// Reviews
// ─────────────────────────────────────────────────────────────────────────────

function reviewsCollection() {
  return getCollection<MarketplaceReviewDoc>('marketplace_reviews', '/id');
}

export async function listReviews(
  listingId: string,
  query: ListReviewsQuery
): Promise<{ items: MarketplaceReviewDoc[]; total: number }> {
  let allDocs = await reviewsCollection().findMany({
    filter: { listingId },
    sort: { createdAt: -1 },
  });

  if (query.verifiedOnly) {
    allDocs = allDocs.filter(d => d.verified);
  }

  // Sorting
  if (query.sortBy === 'helpful') {
    allDocs.sort((a, b) => (query.sortOrder === 'asc' ? a.helpful - b.helpful : b.helpful - a.helpful));
  } else if (query.sortBy === 'rating') {
    allDocs.sort((a, b) => (query.sortOrder === 'asc' ? a.rating - b.rating : b.rating - a.rating));
  } else {
    // newest
    allDocs.sort((a, b) =>
      query.sortOrder === 'asc'
        ? new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()
        : new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
    );
  }

  const total = allDocs.length;
  const items = allDocs.slice(query.offset, query.offset + query.limit);

  return { items, total };
}

export async function getReviewById(id: string): Promise<MarketplaceReviewDoc | null> {
  try {
    return await reviewsCollection().findById(id, id);
  } catch {
    return null;
  }
}

export async function getReviewByUserAndListing(
  userId: string,
  listingId: string
): Promise<MarketplaceReviewDoc | null> {
  return reviewsCollection().findOne({
    filter: { authorId: userId, listingId },
  });
}

export async function createReview(doc: MarketplaceReviewDoc): Promise<MarketplaceReviewDoc> {
  return reviewsCollection().create(doc);
}

export async function updateReview(
  id: string,
  updates: Partial<MarketplaceReviewDoc>
): Promise<MarketplaceReviewDoc | null> {
  try {
    return await reviewsCollection().update(id, id, { ...updates, updatedAt: new Date().toISOString() });
  } catch {
    return null;
  }
}

export async function deleteReview(id: string): Promise<boolean> {
  try {
    await reviewsCollection().delete(id, id);
    return true;
  } catch {
    return false;
  }
}

export async function incrementReviewHelpful(id: string, delta: number): Promise<void> {
  const review = await getReviewById(id);
  if (review) {
    await updateReview(id, { helpful: Math.max(0, review.helpful + delta) });
  }
}

// ─────────────────────────────────────────────────────────────────────────────
// Installs
// ─────────────────────────────────────────────────────────────────────────────

function installsCollection() {
  return getCollection<MarketplaceInstallDoc>('marketplace_installs', '/id');
}

export async function listInstalls(
  userId: string,
  query: ListInstallsQuery
): Promise<{ items: MarketplaceInstallDoc[]; total: number }> {
  const filter: FilterMap = { userId };
  if (query.productId) filter.productId = query.productId;

  const allDocs = await installsCollection().findMany({
    filter,
    sort: { installedAt: -1 },
  });

  const total = allDocs.length;
  const items = allDocs.slice(query.offset, query.offset + query.limit);

  return { items, total };
}

export async function getInstallByUserAndListing(
  userId: string,
  listingId: string
): Promise<MarketplaceInstallDoc | null> {
  return installsCollection().findOne({
    filter: { userId, listingId },
  });
}

export async function createInstall(doc: MarketplaceInstallDoc): Promise<MarketplaceInstallDoc> {
  return installsCollection().create(doc);
}

export async function uninstall(id: string): Promise<boolean> {
  try {
    await installsCollection().update(id, id, { uninstalledAt: new Date().toISOString() });
    return true;
  } catch {
    return false;
  }
}

export async function countInstallsByListing(listingId: string): Promise<number> {
  return installsCollection().count({ listingId, uninstalledAt: null });
}

export async function hasUserInstalled(userId: string, listingId: string): Promise<boolean> {
  const install = await getInstallByUserAndListing(userId, listingId);
  return install !== null && install.uninstalledAt === null;
}

// ─────────────────────────────────────────────────────────────────────────────
// Certifications
// ─────────────────────────────────────────────────────────────────────────────

function certificationsCollection() {
  return getCollection<MarketplaceCertificationDoc>('marketplace_certifications', '/id');
}

export async function listCertifications(listingId: string): Promise<MarketplaceCertificationDoc[]> {
  return certificationsCollection().findMany({
    filter: { listingId },
    sort: { createdAt: -1 },
  });
}

export async function createCertification(doc: MarketplaceCertificationDoc): Promise<MarketplaceCertificationDoc> {
  return certificationsCollection().create(doc);
}

// ─────────────────────────────────────────────────────────────────────────────
// Reports
// ─────────────────────────────────────────────────────────────────────────────

function reportsCollection() {
  return getCollection<MarketplaceReportDoc>('marketplace_reports', '/id');
}

export async function listReports(query: ListReportsQuery): Promise<{ items: MarketplaceReportDoc[]; total: number }> {
  const filter: FilterMap = {};
  if (query.productId) filter.productId = query.productId;
  if (query.status) filter.status = query.status;

  const allDocs = await reportsCollection().findMany({
    filter,
    sort: { createdAt: -1 },
  });

  const total = allDocs.length;
  const items = allDocs.slice(query.offset, query.offset + query.limit);

  return { items, total };
}

export async function getReportById(id: string): Promise<MarketplaceReportDoc | null> {
  try {
    return await reportsCollection().findById(id, id);
  } catch {
    return null;
  }
}

export async function createReport(doc: MarketplaceReportDoc): Promise<MarketplaceReportDoc> {
  return reportsCollection().create(doc);
}

export async function updateReport(
  id: string,
  updates: Partial<MarketplaceReportDoc>
): Promise<MarketplaceReportDoc | null> {
  try {
    const updateData: Record<string, unknown> = { ...updates };
    if (updates.status === 'resolved' || updates.status === 'dismissed') {
      updateData.resolvedAt = new Date().toISOString();
    }
    return await reportsCollection().update(id, id, updateData);
  } catch {
    return null;
  }
}

export async function listMyReports(reporterId: string, query?: ListMyReportsQuery): Promise<MarketplaceReportDoc[]> {
  const filter: FilterMap = { reporterId };
  if (query?.productId) filter.productId = query.productId;

  return reportsCollection().findMany({
    filter,
    sort: { createdAt: -1 },
  });
}

// ─────────────────────────────────────────────────────────────────────────────
// Votes (to prevent duplicate voting)
// ─────────────────────────────────────────────────────────────────────────────

function votesCollection() {
  return getCollection<MarketplaceVoteDoc>('marketplace_votes', '/id');
}

export async function getVoteByUserAndListing(
  userId: string,
  listingId: string
): Promise<MarketplaceVoteDoc | null> {
  return votesCollection().findOne({
    filter: { userId, listingId },
  });
}

export async function createVote(doc: MarketplaceVoteDoc): Promise<MarketplaceVoteDoc> {
  return votesCollection().create(doc);
}

export async function deleteVote(id: string): Promise<boolean> {
  try {
    await votesCollection().delete(id, id);
    return true;
  } catch {
    return false;
  }
}

// ─────────────────────────────────────────────────────────────────────────────
// Aggregate Stats
// ─────────────────────────────────────────────────────────────────────────────

export async function getMarketplaceStats(productId?: string): Promise<{
  totalListings: number;
  publishedListings: number;
  pendingCertification: number;
  totalInstalls: number;
  totalReviews: number;
  averageRating: number;
}> {
  const listingFilter: FilterMap = productId ? { productId } : {};

  const allListings = await listingsCollection().findMany({ filter: listingFilter });
  const publishedListings = allListings.filter(l => l.certificationStatus === 'approved' && l.visibility === 'public');
  const pendingCertification = allListings.filter(l => l.certificationStatus === 'submitted');

  // Calculate aggregate stats
  const totalReviews = publishedListings.reduce((sum, l) => sum + l.reviewCount, 0);
  const ratingSum = publishedListings.reduce((sum, l) => sum + l.averageRating * l.reviewCount, 0);
  const averageRating = totalReviews > 0 ? ratingSum / totalReviews : 0;

  // Count installs
  const installFilter: FilterMap = { uninstalledAt: null };
  if (productId) {
    // Get listing IDs for this product first
    const productListingIds = new Set(publishedListings.map(l => l.id));
    const allInstalls = await installsCollection().findMany({ filter: installFilter });
    const totalInstalls = allInstalls.filter(i => productListingIds.has(i.listingId)).length;

    return {
      totalListings: allListings.length,
      publishedListings: publishedListings.length,
      pendingCertification: pendingCertification.length,
      totalInstalls,
      totalReviews,
      averageRating: Math.round(averageRating * 100) / 100,
    };
  }

  const totalInstalls = await installsCollection().count(installFilter);

  return {
    totalListings: allListings.length,
    publishedListings: publishedListings.length,
    pendingCertification: pendingCertification.length,
    totalInstalls,
    totalReviews,
    averageRating: Math.round(averageRating * 100) / 100,
  };
}
