import { Effect, Schema } from "effect";

import type { AttemptOutcome } from "./resolver";

const Text = Schema.String.check(
  Schema.makeFilter((value) => value.trim().length > 0, {
    expected: "a non-empty string",
  })
);

export const HarnessEventKind = Schema.Literals([
  "started",
  "progress",
  "question",
  "log",
  "completed",
  "failed",
  "cancelled",
]);
export type HarnessEventKind = typeof HarnessEventKind.Type;

export const HarnessEvent = Schema.Struct({
  kind: HarnessEventKind,
  message: Text,
  metadata: Schema.Record(Schema.String, Schema.String),
  occurredAt: Schema.Number,
  sequence: Schema.Int,
});
export type HarnessEvent = typeof HarnessEvent.Type;

export const FakeScenario = Schema.Literals([
  "success",
  "transient-failure-then-success",
  "needs-input",
  "permanent-failure",
  "cancelled",
]);
export type FakeScenario = typeof FakeScenario.Type;

export interface HarnessRuntime {
  readonly name: string;
  readonly run: (input: {
    readonly attemptNumber?: number;
    readonly scenario: FakeScenario;
    readonly signal?: AbortSignal;
  }) => Effect.Effect<readonly [readonly HarnessEvent[], AttemptOutcome]>;
}

const event = (
  sequence: number,
  kind: HarnessEventKind,
  message: string,
  occurredAt: number
): HarnessEvent => ({ kind, message, metadata: {}, occurredAt, sequence });

export const FakeHarnessLive = (
  now: () => number = Date.now
): HarnessRuntime => ({
  name: "fake",
  run: ({ attemptNumber = 1, scenario, signal }) =>
    Effect.sync(() => {
      const started = now();
      const events: HarnessEvent[] = [
        event(0, "started", `fake harness started: ${scenario}`, started),
      ];
      if (signal?.aborted || scenario === "cancelled") {
        events.push(event(1, "cancelled", "simulation cancelled", now()));
        return [
          events,
          {
            classification: "Cancelled",
            retryable: false,
            summary: "Simulation cancelled",
          },
        ] as const;
      }
      events.push(
        event(1, "progress", "validated approved Definition and Design", now())
      );
      switch (scenario) {
        case "success": {
          events.push(
            event(
              2,
              "completed",
              "fake implementation evidence recorded",
              now()
            )
          );
          return [
            events,
            {
              classification: "Succeeded",
              retryable: false,
              summary: "Simulation succeeded; no implementation was claimed",
            },
          ] as const;
        }
        case "transient-failure-then-success": {
          if (attemptNumber > 1) {
            events.push(
              event(2, "completed", "transient failure recovered", now())
            );
            return [
              events,
              {
                classification: "Succeeded",
                retryable: false,
                summary:
                  "Simulation recovered after a transient failure; no implementation was claimed",
              },
            ] as const;
          }
          events.push(event(2, "failed", "transient provider failure", now()));
          return [
            events,
            {
              classification: "RetryableFailure",
              retryable: true,
              summary: "Transient simulation failure",
            },
          ] as const;
        }
        case "needs-input": {
          events.push(
            event(2, "question", "simulation needs a human decision", now())
          );
          return [
            events,
            {
              classification: "NeedsInput",
              retryable: false,
              summary: "Simulation needs input",
            },
          ] as const;
        }
        case "permanent-failure": {
          events.push(
            event(
              2,
              "failed",
              "deterministic permanent simulation failure",
              now()
            )
          );
          return [
            events,
            {
              classification: "PermanentFailure",
              retryable: false,
              summary: "Permanent simulation failure",
            },
          ] as const;
        }
        default: {
          return [
            events,
            {
              classification: "PermanentFailure",
              retryable: false,
              summary: "Unknown simulation scenario",
            },
          ] as const;
        }
      }
    }),
});
