// Offer Service for Marketplace
// Handles offer management for marketplace listings

import {
  Offer,
  OfferStatus,
  Listing,
  ListingStatus,
  CreateOfferInput,
} from './types';
import { offerStore, listingStore, generateId } from './store';

export class OfferService {
  /**
   * Create a new offer on a listing
   */
  async createOffer(
    buyerId: string,
    buyerName: string,
    input: CreateOfferInput
  ): Promise<Offer> {
    const listing = listingStore.getById(input.listingId);

    if (!listing) {
      throw new Error('Listing not found');
    }

    if (listing.status !== ListingStatus.ACTIVE) {
      throw new Error('Cannot make offers on inactive listings');
    }

    if (listing.sellerId === buyerId) {
      throw new Error('Cannot make an offer on your own listing');
    }

    // Check for existing pending offer from this buyer
    const existingOffer = offerStore.getByListingId(input.listingId)
      .find(o => o.buyerId === buyerId && o.status === OfferStatus.PENDING);

    if (existingOffer) {
      throw new Error('You already have a pending offer on this listing');
    }

    const offer: Offer = {
      id: generateId(),
      listingId: input.listingId,
      buyerId,
      buyerName,
      amount: input.amount,
      message: input.message,
      status: OfferStatus.PENDING,
      createdAt: new Date(),
      updatedAt: new Date(),
      expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days
    };

    return offerStore.create(offer);
  }

  /**
   * Get an offer by ID
   */
  async getOfferById(id: string): Promise<Offer | null> {
    return offerStore.getById(id) || null;
  }

  /**
   * Get all offers for a listing
   */
  async getOffersForListing(listingId: string): Promise<Offer[]> {
    return offerStore.getByListingId(listingId);
  }

  /**
   * Get all offers made by a buyer
   */
  async getOffersByBuyer(buyerId: string): Promise<Offer[]> {
    return offerStore.getByBuyerId(buyerId);
  }

  /**
   * Get all offers for a seller's listings
   */
  async getOffersForSeller(sellerId: string): Promise<(Offer & { listing?: Listing })[]> {
    const sellerListings = listingStore.getBySellerId(sellerId);
    const listingIds = new Set(sellerListings.map(l => l.id));

    const offers = offerStore.getAll().filter(o => listingIds.has(o.listingId));

    return offers.map(offer => ({
      ...offer,
      listing: listingStore.getById(offer.listingId),
    }));
  }

  /**
   * Accept an offer
   */
  async acceptOffer(offerId: string, sellerId: string): Promise<Offer> {
    const offer = offerStore.getById(offerId);

    if (!offer) {
      throw new Error('Offer not found');
    }

    const listing = listingStore.getById(offer.listingId);

    if (!listing) {
      throw new Error('Listing not found');
    }

    if (listing.sellerId !== sellerId) {
      throw new Error('Unauthorized: You do not own this listing');
    }

    if (offer.status !== OfferStatus.PENDING) {
      throw new Error('Can only accept pending offers');
    }

    // Accept this offer
    const acceptedOffer = offerStore.update(offerId, {
      status: OfferStatus.ACCEPTED
    });

    // Reject all other pending offers for this listing
    const otherOffers = offerStore.getByListingId(offer.listingId)
      .filter(o => o.id !== offerId && o.status === OfferStatus.PENDING);

    for (const otherOffer of otherOffers) {
      offerStore.update(otherOffer.id, { status: OfferStatus.REJECTED });
    }

    // Mark listing as sold
    listingStore.update(offer.listingId, {
      status: ListingStatus.SOLD,
      quantity: 0
    });

    if (!acceptedOffer) {
      throw new Error('Failed to accept offer');
    }

    return acceptedOffer;
  }

  /**
   * Reject an offer
   */
  async rejectOffer(offerId: string, sellerId: string): Promise<Offer> {
    const offer = offerStore.getById(offerId);

    if (!offer) {
      throw new Error('Offer not found');
    }

    const listing = listingStore.getById(offer.listingId);

    if (!listing) {
      throw new Error('Listing not found');
    }

    if (listing.sellerId !== sellerId) {
      throw new Error('Unauthorized: You do not own this listing');
    }

    if (offer.status !== OfferStatus.PENDING) {
      throw new Error('Can only reject pending offers');
    }

    const rejectedOffer = offerStore.update(offerId, {
      status: OfferStatus.REJECTED
    });

    if (!rejectedOffer) {
      throw new Error('Failed to reject offer');
    }

    return rejectedOffer;
  }

  /**
   * Withdraw an offer (buyer action)
   */
  async withdrawOffer(offerId: string, buyerId: string): Promise<Offer> {
    const offer = offerStore.getById(offerId);

    if (!offer) {
      throw new Error('Offer not found');
    }

    if (offer.buyerId !== buyerId) {
      throw new Error('Unauthorized: This is not your offer');
    }

    if (offer.status !== OfferStatus.PENDING) {
      throw new Error('Can only withdraw pending offers');
    }

    const withdrawnOffer = offerStore.update(offerId, {
      status: OfferStatus.WITHDRAWN
    });

    if (!withdrawnOffer) {
      throw new Error('Failed to withdraw offer');
    }

    return withdrawnOffer;
  }

  /**
   * Counter offer (update amount)
   */
  async updateOfferAmount(
    offerId: string,
    buyerId: string,
    newAmount: number
  ): Promise<Offer> {
    const offer = offerStore.getById(offerId);

    if (!offer) {
      throw new Error('Offer not found');
    }

    if (offer.buyerId !== buyerId) {
      throw new Error('Unauthorized: This is not your offer');
    }

    if (offer.status !== OfferStatus.PENDING) {
      throw new Error('Can only update pending offers');
    }

    const updatedOffer = offerStore.update(offerId, { amount: newAmount });

    if (!updatedOffer) {
      throw new Error('Failed to update offer');
    }

    return updatedOffer;
  }

  /**
   * Get offer statistics
   */
  async getOfferStats(userId: string, role: 'buyer' | 'seller'): Promise<{
    totalOffers: number;
    pendingOffers: number;
    acceptedOffers: number;
    rejectedOffers: number;
  }> {
    let offers: Offer[];

    if (role === 'buyer') {
      offers = offerStore.getByBuyerId(userId);
    } else {
      const sellerListings = listingStore.getBySellerId(userId);
      const listingIds = new Set(sellerListings.map(l => l.id));
      offers = offerStore.getAll().filter(o => listingIds.has(o.listingId));
    }

    return {
      totalOffers: offers.length,
      pendingOffers: offers.filter(o => o.status === OfferStatus.PENDING).length,
      acceptedOffers: offers.filter(o => o.status === OfferStatus.ACCEPTED).length,
      rejectedOffers: offers.filter(o => o.status === OfferStatus.REJECTED).length,
    };
  }

  /**
   * Expire old pending offers
   */
  async expireOldOffers(): Promise<number> {
    const now = new Date();
    let expiredCount = 0;

    const pendingOffers = offerStore.getAll().filter(
      o => o.status === OfferStatus.PENDING && o.expiresAt
    );

    for (const offer of pendingOffers) {
      if (offer.expiresAt && offer.expiresAt < now) {
        offerStore.update(offer.id, { status: OfferStatus.EXPIRED });
        expiredCount++;
      }
    }

    return expiredCount;
  }
}

// Export singleton instance
export const offerService = new OfferService();
