import { describe, it, expect } from 'vitest';
import { Result } from '../result';

describe('Result', () => {
  describe('ok', () => {
    it('should create a successful result', () => {
      const result = Result.ok(42);
      expect(result.isOk).toBe(true);
      expect(result.isErr).toBe(false);
      expect(result.unwrap()).toBe(42);
    });
  });

  describe('err', () => {
    it('should create an error result', () => {
      const error = new Error('fail');
      const result = Result.err(error);
      expect(result.isOk).toBe(false);
      expect(result.isErr).toBe(true);
      expect(result.unwrapErr()).toBe(error);
    });
  });

  describe('unwrap', () => {
    it('should throw on error result', () => {
      const result = Result.err(new Error('boom'));
      expect(() => result.unwrap()).toThrow('boom');
    });
  });

  describe('unwrapOr', () => {
    it('should return value on ok', () => {
      expect(Result.ok(10).unwrapOr(0)).toBe(10);
    });

    it('should return default on err', () => {
      expect(Result.err<number>(new Error('x')).unwrapOr(0)).toBe(0);
    });
  });

  describe('map', () => {
    it('should transform ok value', () => {
      const result = Result.ok(5).map((v) => v * 2);
      expect(result.unwrap()).toBe(10);
    });

    it('should pass through error', () => {
      const error = new Error('oops');
      const result = Result.err<number>(error).map((v) => v * 2);
      expect(result.unwrapErr()).toBe(error);
    });
  });

  describe('andThen', () => {
    it('should chain ok results', () => {
      const result = Result.ok(5).andThen((v) =>
        v > 0 ? Result.ok(v * 2) : Result.err(new Error('negative')),
      );
      expect(result.unwrap()).toBe(10);
    });

    it('should short-circuit on error', () => {
      const result = Result.err<number>(new Error('first')).andThen((v) => Result.ok(v * 2));
      expect(result.unwrapErr().message).toBe('first');
    });
  });

  describe('match', () => {
    it('should call ok handler on success', () => {
      const output = Result.ok(42).match({
        ok: (v) => `value: ${v}`,
        err: (e) => `error: ${e.message}`,
      });
      expect(output).toBe('value: 42');
    });

    it('should call err handler on failure', () => {
      const output = Result.err(new Error('bad')).match({
        ok: () => 'ok',
        err: (e) => `error: ${e.message}`,
      });
      expect(output).toBe('error: bad');
    });
  });
});
