import { describe, expect, it } from 'vitest';
import { CreateProductVariant } from '../application/variant-use-cases.js';
import type { PricingServicePort, PriceCalculation, VariantPrice } from '../../pricing/index.js';
import type { NewProductVariant, ProductVariant } from '../domain/variant.js';
import type { Product } from '../domain/product.js';
import type {
  ProductRepository,
  ProductVariantRepository,
} from '../domain/ports.js';

function makeVariant(productId: string, sku: string): ProductVariant {
  return {
    id: 'variant-1',
    productId,
    sku,
    ean: null,
    attributes: {},
    createdAt: new Date('2026-01-01T00:00:00Z'),
    updatedAt: new Date('2026-01-01T00:00:00Z'),
  };
}

class FakeProductRepository implements ProductRepository {
  public readonly existing = new Set<string>();
  async findById(id: string): Promise<Product | undefined> {
    return this.existing.has(id) ? ({ id } as unknown as Product) : undefined;
  }
  async create(): Promise<Product> {
    throw new Error('not implemented');
  }
  async findActiveBySlug(): Promise<Product | undefined> {
    throw new Error('not implemented');
  }
  async update(): Promise<Product | undefined> {
    throw new Error('not implemented');
  }
  async delete(): Promise<void> {
    throw new Error('not implemented');
  }
  async listAll(): Promise<{ items: Product[]; total: number }> {
    throw new Error('not implemented');
  }
  async categoriesExist(): Promise<boolean> {
    throw new Error('not implemented');
  }
  async brandExists(): Promise<boolean> {
    throw new Error('not implemented');
  }
}

class FakeProductVariantRepository implements ProductVariantRepository {
  public lastInput: NewProductVariant | undefined;
  constructor(public readonly variant: ProductVariant = makeVariant('p', 'SKU-X')) {}
  async create(_productId: string, input: NewProductVariant): Promise<ProductVariant> {
    this.lastInput = input;
    return this.variant;
  }
  async listByProductId(): Promise<ProductVariant[]> {
    throw new Error('not implemented');
  }
  async update(): Promise<ProductVariant | undefined> {
    throw new Error('not implemented');
  }
}

class FakePricingService implements PricingServicePort {
  public seedCalls: string[] = [];
  public seedShouldThrow = false;
  async seedVariantPrice(variantId: string): Promise<void> {
    this.seedCalls.push(variantId);
    if (this.seedShouldThrow) throw new Error('seed boom');
  }
  async calculate(): Promise<PriceCalculation> {
    throw new Error('not implemented');
  }
  async getVariantPrice(): Promise<VariantPrice | undefined> {
    throw new Error('not implemented');
  }
  async setVariantPrice(): Promise<VariantPrice> {
    throw new Error('not implemented');
  }
}

describe('CreateProductVariant', () => {
  it('seeds a default price row immediately after creating the variant (F-138 AC1, AC4)', async () => {
    const products = new FakeProductRepository();
    products.existing.add('product-1');
    const variant = makeVariant('product-1', 'SKU-X');
    const variants = new FakeProductVariantRepository(variant);
    const pricing = new FakePricingService();
    const usecase = new CreateProductVariant(products, variants, pricing);

    const result = await usecase.execute('product-1', { sku: 'SKU-X' });

    expect(result).toBe(variant);
    expect(variants.lastInput?.sku).toBe('SKU-X');
    expect(pricing.seedCalls).toEqual(['variant-1']);
  });

  it('does not seed a price row when the product does not exist (F-138 AC1)', async () => {
    const products = new FakeProductRepository();
    const variants = new FakeProductVariantRepository();
    const pricing = new FakePricingService();
    const usecase = new CreateProductVariant(products, variants, pricing);

    const result = await usecase.execute('product-2', { sku: 'SKU-X' });

    expect(result).toBeUndefined();
    expect(pricing.seedCalls).toEqual([]);
  });

  it('returns the created variant even if seeding the price row fails (F-138 AC3, best-effort)', async () => {
    const products = new FakeProductRepository();
    products.existing.add('product-1');
    const variant = makeVariant('product-1', 'SKU-X');
    const variants = new FakeProductVariantRepository(variant);
    const pricing = new FakePricingService();
    pricing.seedShouldThrow = true;
    const usecase = new CreateProductVariant(products, variants, pricing);

    const result = await usecase.execute('product-1', { sku: 'SKU-X' });

    expect(result).toBe(variant);
    expect(pricing.seedCalls).toEqual(['variant-1']);
  });
});
