/**
 * Demand & Market Matching Database Service
 * CRUD operations for consumer preferences, demand signals, and market matching
 */

import prisma from './prisma';
import type {
  ConsumerPreference,
  DemandSignal,
  SupplyCommitment,
  MarketMatch,
  SeasonalPlan,
  DemandForecast,
  PlantingRecommendation,
  SupplyStatus,
  CommitmentStatus,
  MatchStatus,
  PlanStatus,
  Prisma,
} from '@prisma/client';
import type { PaginationOptions, PaginatedResult, LocationFilter, DateRangeFilter } from './types';
import { createPaginatedResult, calculateDistanceKm } from './types';

// ============================================
// CONSUMER PREFERENCE OPERATIONS
// ============================================

// Create or update consumer preference
export async function upsertConsumerPreference(data: {
  consumerId: string;
  latitude: number;
  longitude: number;
  maxDeliveryRadiusKm?: number;
  city?: string;
  region?: string;
  dietaryType?: string[];
  allergies?: string[];
  dislikes?: string[];
  preferredCategories?: string[];
  preferredItems?: Record<string, unknown>[];
  certificationPreferences?: string[];
  freshnessImportance?: number;
  priceImportance?: number;
  sustainabilityImportance?: number;
  deliveryPreferences?: Record<string, unknown>;
  householdSize?: number;
  weeklyBudget?: number;
  currency?: string;
}): Promise<ConsumerPreference> {
  return prisma.consumerPreference.upsert({
    where: { consumerId: data.consumerId },
    update: {
      ...data,
      updatedAt: new Date(),
    },
    create: {
      ...data,
      maxDeliveryRadiusKm: data.maxDeliveryRadiusKm || 50,
      dietaryType: data.dietaryType || [],
      allergies: data.allergies || [],
      dislikes: data.dislikes || [],
      preferredCategories: data.preferredCategories || [],
      certificationPreferences: data.certificationPreferences || [],
      freshnessImportance: data.freshnessImportance || 3,
      priceImportance: data.priceImportance || 3,
      sustainabilityImportance: data.sustainabilityImportance || 3,
      householdSize: data.householdSize || 1,
      currency: data.currency || 'USD',
    },
  });
}

// Get consumer preference by consumer ID
export async function getConsumerPreference(consumerId: string): Promise<ConsumerPreference | null> {
  return prisma.consumerPreference.findUnique({
    where: { consumerId },
  });
}

// Get consumer preferences near location
export async function getNearbyConsumerPreferences(
  location: LocationFilter
): Promise<ConsumerPreference[]> {
  const preferences = await prisma.consumerPreference.findMany();

  return preferences.filter(pref => {
    const distance = calculateDistanceKm(
      location.latitude,
      location.longitude,
      pref.latitude,
      pref.longitude
    );
    return distance <= location.radiusKm;
  });
}

// ============================================
// DEMAND SIGNAL OPERATIONS
// ============================================

// Create a demand signal
export async function createDemandSignal(data: {
  centerLat: number;
  centerLon: number;
  radiusKm: number;
  regionName: string;
  periodStart: Date;
  periodEnd: Date;
  seasonalPeriod: string;
  demandItems: Record<string, unknown>[];
  totalConsumers: number;
  totalWeeklyDemandKg: number;
  confidenceLevel: number;
  currentSupplyKg?: number;
  supplyGapKg?: number;
  supplyStatus?: SupplyStatus;
}): Promise<DemandSignal> {
  return prisma.demandSignal.create({
    data: {
      ...data,
      supplyStatus: data.supplyStatus || 'BALANCED',
    },
  });
}

// Get demand signal by ID
export async function getDemandSignalById(id: string): Promise<DemandSignal | null> {
  return prisma.demandSignal.findUnique({
    where: { id },
  });
}

// Get demand signals with pagination
export async function getDemandSignals(
  options: PaginationOptions = {},
  filters?: {
    regionName?: string;
    supplyStatus?: SupplyStatus;
    dateRange?: DateRangeFilter;
  }
): Promise<PaginatedResult<DemandSignal>> {
  const page = options.page || 1;
  const limit = options.limit || 20;
  const skip = (page - 1) * limit;

  const where: Prisma.DemandSignalWhereInput = {};
  if (filters?.regionName) where.regionName = { contains: filters.regionName, mode: 'insensitive' };
  if (filters?.supplyStatus) where.supplyStatus = filters.supplyStatus;
  if (filters?.dateRange) {
    where.periodStart = { gte: filters.dateRange.start };
    where.periodEnd = { lte: filters.dateRange.end };
  }

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

  return createPaginatedResult(signals, total, page, limit);
}

// Get active demand signals for region
export async function getActiveDemandSignals(
  location: LocationFilter
): Promise<DemandSignal[]> {
  const now = new Date();
  const signals = await prisma.demandSignal.findMany({
    where: {
      periodEnd: { gte: now },
    },
    orderBy: { timestamp: 'desc' },
  });

  return signals.filter(signal => {
    const distance = calculateDistanceKm(
      location.latitude,
      location.longitude,
      signal.centerLat,
      signal.centerLon
    );
    return distance <= signal.radiusKm;
  });
}

// ============================================
// SUPPLY COMMITMENT OPERATIONS
// ============================================

// Create a supply commitment
export async function createSupplyCommitment(data: {
  growerId: string;
  produceType: string;
  variety?: string;
  committedQuantityKg: number;
  availableFrom: Date;
  availableUntil: Date;
  pricePerKg: number;
  currency?: string;
  minimumOrderKg?: number;
  bulkDiscountThreshold?: number;
  bulkDiscountPercent?: number;
  certifications?: string[];
  freshnessGuaranteeHours?: number;
  deliveryRadiusKm: number;
  deliveryMethods: string[];
}): Promise<SupplyCommitment> {
  return prisma.supplyCommitment.create({
    data: {
      ...data,
      currency: data.currency || 'USD',
      minimumOrderKg: data.minimumOrderKg || 0,
      certifications: data.certifications || [],
      status: 'AVAILABLE',
      remainingKg: data.committedQuantityKg,
    },
  });
}

// Get supply commitment by ID
export async function getSupplyCommitmentById(id: string): Promise<SupplyCommitment | null> {
  return prisma.supplyCommitment.findUnique({
    where: { id },
  });
}

// Update supply commitment
export async function updateSupplyCommitment(
  id: string,
  data: Prisma.SupplyCommitmentUpdateInput
): Promise<SupplyCommitment> {
  return prisma.supplyCommitment.update({
    where: { id },
    data,
  });
}

// Reduce commitment quantity
export async function reduceCommitmentQuantity(
  id: string,
  quantityKg: number
): Promise<SupplyCommitment> {
  const commitment = await prisma.supplyCommitment.findUnique({
    where: { id },
  });

  if (!commitment) throw new Error('Commitment not found');

  const newRemainingKg = commitment.remainingKg - quantityKg;
  let newStatus: CommitmentStatus = commitment.status;

  if (newRemainingKg <= 0) {
    newStatus = 'FULLY_COMMITTED';
  } else if (newRemainingKg < commitment.committedQuantityKg) {
    newStatus = 'PARTIALLY_COMMITTED';
  }

  return prisma.supplyCommitment.update({
    where: { id },
    data: {
      remainingKg: Math.max(0, newRemainingKg),
      status: newStatus,
    },
  });
}

// Get supply commitments with pagination
export async function getSupplyCommitments(
  options: PaginationOptions = {},
  filters?: {
    growerId?: string;
    produceType?: string;
    status?: CommitmentStatus;
    dateRange?: DateRangeFilter;
  }
): Promise<PaginatedResult<SupplyCommitment>> {
  const page = options.page || 1;
  const limit = options.limit || 20;
  const skip = (page - 1) * limit;

  const where: Prisma.SupplyCommitmentWhereInput = {};
  if (filters?.growerId) where.growerId = filters.growerId;
  if (filters?.produceType) where.produceType = { contains: filters.produceType, mode: 'insensitive' };
  if (filters?.status) where.status = filters.status;
  if (filters?.dateRange) {
    where.availableFrom = { lte: filters.dateRange.end };
    where.availableUntil = { gte: filters.dateRange.start };
  }

  const [commitments, total] = await Promise.all([
    prisma.supplyCommitment.findMany({
      where,
      skip,
      take: limit,
      orderBy: { timestamp: 'desc' },
      include: { grower: true },
    }),
    prisma.supplyCommitment.count({ where }),
  ]);

  return createPaginatedResult(commitments, total, page, limit);
}

// Find matching supply for demand
export async function findMatchingSupply(
  produceType: string,
  location: LocationFilter,
  requiredDate: Date
): Promise<SupplyCommitment[]> {
  const commitments = await prisma.supplyCommitment.findMany({
    where: {
      produceType: { contains: produceType, mode: 'insensitive' },
      status: { in: ['AVAILABLE', 'PARTIALLY_COMMITTED'] },
      availableFrom: { lte: requiredDate },
      availableUntil: { gte: requiredDate },
      remainingKg: { gt: 0 },
    },
    include: { grower: true },
  });

  // Filter by delivery radius
  return commitments.filter(c => {
    if (!c.grower.latitude || !c.grower.longitude) return false;
    const distance = calculateDistanceKm(
      location.latitude,
      location.longitude,
      c.grower.latitude,
      c.grower.longitude
    );
    return distance <= c.deliveryRadiusKm;
  });
}

// ============================================
// MARKET MATCH OPERATIONS
// ============================================

// Create a market match
export async function createMarketMatch(data: {
  consumerId: string;
  growerId: string;
  demandSignalId: string;
  supplyCommitmentId: string;
  produceType: string;
  matchedQuantityKg: number;
  agreedPricePerKg: number;
  totalPrice: number;
  currency?: string;
  deliveryDate: Date;
  deliveryMethod: string;
  deliveryLatitude?: number;
  deliveryLongitude?: number;
  deliveryAddress?: string;
}): Promise<MarketMatch> {
  // Reduce the supply commitment
  await reduceCommitmentQuantity(data.supplyCommitmentId, data.matchedQuantityKg);

  return prisma.marketMatch.create({
    data: {
      ...data,
      currency: data.currency || 'USD',
      status: 'PENDING',
    },
  });
}

// Get market match by ID
export async function getMarketMatchById(id: string): Promise<MarketMatch | null> {
  return prisma.marketMatch.findUnique({
    where: { id },
  });
}

// Get market match with details
export async function getMarketMatchWithDetails(id: string) {
  return prisma.marketMatch.findUnique({
    where: { id },
    include: {
      demandSignal: true,
      supplyCommitment: {
        include: { grower: true },
      },
    },
  });
}

// Update market match status
export async function updateMarketMatchStatus(
  id: string,
  status: MatchStatus,
  extras?: {
    consumerRating?: number;
    growerRating?: number;
    feedback?: string;
  }
): Promise<MarketMatch> {
  return prisma.marketMatch.update({
    where: { id },
    data: { status, ...extras },
  });
}

// Get market matches with pagination
export async function getMarketMatches(
  options: PaginationOptions = {},
  filters?: {
    consumerId?: string;
    growerId?: string;
    status?: MatchStatus;
    dateRange?: DateRangeFilter;
  }
): Promise<PaginatedResult<MarketMatch>> {
  const page = options.page || 1;
  const limit = options.limit || 20;
  const skip = (page - 1) * limit;

  const where: Prisma.MarketMatchWhereInput = {};
  if (filters?.consumerId) where.consumerId = filters.consumerId;
  if (filters?.growerId) where.growerId = filters.growerId;
  if (filters?.status) where.status = filters.status;
  if (filters?.dateRange) {
    where.deliveryDate = {
      gte: filters.dateRange.start,
      lte: filters.dateRange.end,
    };
  }

  const [matches, total] = await Promise.all([
    prisma.marketMatch.findMany({
      where,
      skip,
      take: limit,
      orderBy: { deliveryDate: 'desc' },
      include: {
        demandSignal: true,
        supplyCommitment: true,
      },
    }),
    prisma.marketMatch.count({ where }),
  ]);

  return createPaginatedResult(matches, total, page, limit);
}

// ============================================
// SEASONAL PLAN OPERATIONS
// ============================================

// Create a seasonal plan
export async function createSeasonalPlan(data: {
  growerId: string;
  year: number;
  season: string;
  location: Record<string, unknown>;
  growingCapacity: Record<string, unknown>;
  plannedCrops: Record<string, unknown>[];
  expectedTotalYieldKg?: number;
  expectedRevenue?: number;
  expectedCarbonFootprintKg?: number;
}): Promise<SeasonalPlan> {
  return prisma.seasonalPlan.create({
    data: {
      ...data,
      status: 'DRAFT',
    },
  });
}

// Get seasonal plan by ID
export async function getSeasonalPlanById(id: string): Promise<SeasonalPlan | null> {
  return prisma.seasonalPlan.findUnique({
    where: { id },
  });
}

// Update seasonal plan
export async function updateSeasonalPlan(
  id: string,
  data: Prisma.SeasonalPlanUpdateInput
): Promise<SeasonalPlan> {
  return prisma.seasonalPlan.update({
    where: { id },
    data,
  });
}

// Update seasonal plan status
export async function updateSeasonalPlanStatus(
  id: string,
  status: PlanStatus,
  completionPercentage?: number
): Promise<SeasonalPlan> {
  return prisma.seasonalPlan.update({
    where: { id },
    data: { status, completionPercentage },
  });
}

// Get seasonal plans by grower
export async function getSeasonalPlansByGrower(
  growerId: string,
  year?: number
): Promise<SeasonalPlan[]> {
  return prisma.seasonalPlan.findMany({
    where: {
      growerId,
      ...(year && { year }),
    },
    orderBy: [{ year: 'desc' }, { season: 'asc' }],
  });
}

// ============================================
// DEMAND FORECAST OPERATIONS
// ============================================

// Create a demand forecast
export async function createDemandForecast(data: {
  region: string;
  forecastPeriodStart: Date;
  forecastPeriodEnd: Date;
  forecasts: Record<string, unknown>[];
  modelVersion: string;
  dataPointsUsed: number;
  lastTrainingDate?: Date;
}): Promise<DemandForecast> {
  return prisma.demandForecast.create({ data });
}

// Get latest demand forecast for region
export async function getLatestDemandForecast(region: string): Promise<DemandForecast | null> {
  return prisma.demandForecast.findFirst({
    where: { region },
    orderBy: { generatedAt: 'desc' },
  });
}

// ============================================
// PLANTING RECOMMENDATION OPERATIONS
// ============================================

// Create a planting recommendation
export async function createPlantingRecommendation(data: {
  growerId: string;
  produceType: string;
  variety?: string;
  category: string;
  recommendedQuantity: number;
  quantityUnit: string;
  expectedYieldKg: number;
  yieldConfidence: number;
  plantByDate: Date;
  expectedHarvestStart: Date;
  expectedHarvestEnd: Date;
  growingDays: number;
  projectedDemandKg?: number;
  projectedPricePerKg?: number;
  projectedRevenue?: number;
  marketConfidence?: number;
  riskFactors?: Record<string, unknown>[];
  overallRisk?: string;
  demandSignalIds: string[];
  explanation?: string;
}): Promise<PlantingRecommendation> {
  return prisma.plantingRecommendation.create({
    data: {
      ...data,
      overallRisk: data.overallRisk || 'medium',
    },
  });
}

// Get planting recommendations for grower
export async function getPlantingRecommendations(
  growerId: string,
  options: PaginationOptions = {}
): Promise<PaginatedResult<PlantingRecommendation>> {
  const page = options.page || 1;
  const limit = options.limit || 20;
  const skip = (page - 1) * limit;

  const [recommendations, total] = await Promise.all([
    prisma.plantingRecommendation.findMany({
      where: { growerId },
      skip,
      take: limit,
      orderBy: { plantByDate: 'asc' },
    }),
    prisma.plantingRecommendation.count({ where: { growerId } }),
  ]);

  return createPaginatedResult(recommendations, total, page, limit);
}
