import { FreshnessUrgency, FreshnessSource, ItemStatus } from '@meshitrack/shared';
import type { StorageLocation } from '@meshitrack/shared';

export interface FreshnessInput {
  status: ItemStatus;
  storageLocation: StorageLocation;
  purchaseDate: Date;
  expirationDate?: Date | null;
  openedDate?: Date | null;
  preparedDate?: Date | null;
}

export interface FreshnessRuleLike {
  shelfLifeDays: number;
  openedLifeDays: number;
  freezerLifeDays?: number | null;
}

export interface FreshnessResult {
  estimatedExpiryDate: Date;
  daysRemaining: number;
  urgency: FreshnessUrgency;
  source: FreshnessSource;
}

const ACTIVE_STATUSES = new Set<string>([
  ItemStatus.SEALED,
  ItemStatus.OPENED,
  ItemStatus.PREPARED,
]);

export class FreshnessCalculatorService {
  public calculate(item: FreshnessInput, rule: FreshnessRuleLike | null): FreshnessResult {
    // If packaging expiration date exists, use it
    if (item.expirationDate) {
      return this.buildResult(new Date(item.expirationDate), FreshnessSource.PACKAGING);
    }

    // If no rule, default to 7 days from purchase
    if (!rule) {
      const fallback = new Date(item.purchaseDate);
      fallback.setDate(fallback.getDate() + 7);
      return this.buildResult(fallback, FreshnessSource.RULE);
    }

    // Freezer uses freezerLifeDays from purchase date
    if (item.storageLocation === 'freezer' && rule.freezerLifeDays) {
      const freezerExpiry = new Date(item.purchaseDate);
      freezerExpiry.setDate(freezerExpiry.getDate() + rule.freezerLifeDays);
      return this.buildResult(freezerExpiry, FreshnessSource.RULE);
    }

    // Opened/prepared items use openedLifeDays from openedDate
    if (
      (item.status === ItemStatus.OPENED || item.status === ItemStatus.PREPARED) &&
      item.openedDate
    ) {
      const openedExpiry = new Date(item.openedDate);
      openedExpiry.setDate(openedExpiry.getDate() + rule.openedLifeDays);
      return this.buildResult(openedExpiry, FreshnessSource.RULE);
    }

    // Sealed: use shelfLifeDays from purchase date
    const sealedExpiry = new Date(item.purchaseDate);
    sealedExpiry.setDate(sealedExpiry.getDate() + rule.shelfLifeDays);
    return this.buildResult(sealedExpiry, FreshnessSource.RULE);
  }

  public isActive(status: string): boolean {
    return ACTIVE_STATUSES.has(status);
  }

  public mapUrgency(daysRemaining: number): FreshnessUrgency {
    if (daysRemaining > 5) return FreshnessUrgency.FRESH;
    if (daysRemaining >= 2) return FreshnessUrgency.USE_SOON;
    if (daysRemaining >= 0) return FreshnessUrgency.URGENT;
    if (daysRemaining >= -3) return FreshnessUrgency.CHECK;
    return FreshnessUrgency.EXPIRED;
  }

  private buildResult(estimatedExpiryDate: Date, source: FreshnessSource): FreshnessResult {
    const now = new Date();
    const diffMs = estimatedExpiryDate.getTime() - now.getTime();
    const daysRemaining = Math.ceil(diffMs / (1000 * 60 * 60 * 24));
    const urgency = this.mapUrgency(daysRemaining);

    return { estimatedExpiryDate, daysRemaining, urgency, source };
  }
}
