import { createServer } from 'node:http';
import { fileURLToPath } from 'node:url';
import { Command, Options } from '@effect/cli';
import { FileSystem, HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiError, HttpApiGroup, HttpApiSchema, HttpMiddleware, HttpRouter, HttpServer, HttpServerResponse, Path } from '@effect/platform';
import { NodeHttpServer } from '@effect/platform-node';
import { AnsiDoc } from '@effect/printer-ansi';
import { Cause, Data, Effect, Layer, Option, Schema, Struct } from 'effect';
import open, { apps } from 'open';
import * as Model from '../services/Model.js';
import * as Typesync from '../services/Typesync.js';
class HypergraphTypesyncStudioApiRouter extends /*#__PURE__*/HttpApiGroup.make('HypergraphTypesyncStudioApiRouter').add(
// exposes an api endpoint at /api/vX/schema/events that is a stream of the current Schema parsed from the directory the hypergraph-cli tool is running in
HttpApiEndpoint.get('HypergraphSchemaEventStream')`/schema/events`.addError(HttpApiError.InternalServerError).addSuccess(Schema.String.pipe(HttpApiSchema.withEncoding({
  kind: 'Json',
  contentType: 'text/event-stream'
})))).add(HttpApiEndpoint.post('SyncHypergraphSchema')`/schema/sync`.setPayload(Model.TypesyncHypergraphSchema).addSuccess(Model.TypesyncHypergraphSchema).addError(HttpApiError.InternalServerError).addError(HttpApiError.BadRequest)).add(HttpApiEndpoint.post('SyncHypergraphMapping')`/mapping/sync`.setPayload(Schema.Struct({
  schema: Model.TypesyncHypergraphSchema,
  mapping: Model.TypesyncHypergraphMapping
})).addSuccess(Model.TypesyncHypergraphSchema).addError(HttpApiError.InternalServerError).addError(HttpApiError.BadRequest)).prefix('/v1') {}
class HypergraphTypesyncStudioApi extends /*#__PURE__*/HttpApi.make('HypergraphTypesyncStudioApi').add(HypergraphTypesyncStudioApiRouter).prefix('/api') {}
const hypergraphTypeSyncApiLive = /*#__PURE__*/HttpApiBuilder.group(HypergraphTypesyncStudioApi, 'HypergraphTypesyncStudioApiRouter', handlers => Effect.gen(function* () {
  const schemaStream = yield* Typesync.TypesyncSchemaStreamBuilder;
  return handlers.handle('HypergraphSchemaEventStream', () => Effect.gen(function* () {
    const stream = yield* schemaStream.hypergraphSchemaStream().pipe(Effect.tapErrorCause(cause => Effect.logError(AnsiDoc.cat(AnsiDoc.text('Failure building Hypergraph events stream:'), AnsiDoc.text(Cause.pretty(cause))))), Effect.catchAll(() => new HttpApiError.InternalServerError()));
    return yield* HttpServerResponse.stream(stream, {
      contentType: 'text/event-stream'
    }).pipe(HttpServerResponse.setHeaders({
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
      Connection: 'keep-alive'
    }));
  })).handle('SyncHypergraphSchema', ({
    payload
  }) => schemaStream.syncSchema(payload).pipe(Effect.tapErrorCause(cause => Effect.logError(AnsiDoc.cat(AnsiDoc.text('Failure syncing Hypergraph Schema:'), AnsiDoc.text(Cause.pretty(cause))))), Effect.catchAll(() => new HttpApiError.InternalServerError()))).handle('SyncHypergraphMapping', ({
    payload
  }) => schemaStream.syncMapping(payload.schema, payload.mapping).pipe(Effect.tapErrorCause(cause => Effect.logError(AnsiDoc.cat(AnsiDoc.text('Failure syncing Hypergraph mapping:'), AnsiDoc.text(Cause.pretty(cause))))), Effect.catchAll(() => new HttpApiError.InternalServerError())));
}));
const HypergraphTypeSyncApiLayer = /*#__PURE__*/HttpApiBuilder.middlewareCors({
  allowedMethods: ['GET', 'POST', 'OPTIONS'],
  allowedOrigins: ['http://localhost:3000', 'http://localhost:5173']
}).pipe(/*#__PURE__*/Layer.provideMerge(/*#__PURE__*/HttpApiBuilder.api(HypergraphTypesyncStudioApi)), /*#__PURE__*/Layer.provide(Typesync.layer), /*#__PURE__*/Layer.provide(hypergraphTypeSyncApiLive));
const HypergraphTypeSyncApiLive = /*#__PURE__*/HttpApiBuilder.httpApp.pipe(/*#__PURE__*/Effect.provide(/*#__PURE__*/Layer.mergeAll(HypergraphTypeSyncApiLayer, HttpApiBuilder.Router.Live, HttpApiBuilder.Middleware.layer)));
const TypesyncStudioFileRouter = /*#__PURE__*/Effect.gen(function* () {
  const fs = yield* FileSystem.FileSystem;
  const path = yield* Path.Path;
  const __filename = fileURLToPath(import.meta.url);
  const __dirname = path.dirname(__filename);
  // Try multiple possible locations for the dist directory
  const possiblePaths = [
  // npm published package (when this file is in node_modules/@graphprotocol/hypergraph/dist/cli/subcommands/)
  path.resolve(__dirname, '..', '..', 'typesync-studio', 'dist'),
  // Development mode (when this file is in packages/hypergraph/src/cli/subcommands/)
  path.resolve(__dirname, '..', '..', '..', '..', 'typesync-studio', 'dist')];
  const findTypesyncStudioDist = Effect.fnUntraced(function* () {
    return yield* Effect.findFirst(possiblePaths, _ => fs.exists(_).pipe(Effect.orElseSucceed(() => false)));
  });
  const typesyncStudioClientDist = yield* findTypesyncStudioDist().pipe(
  // default to first path
  Effect.map(maybe => Option.getOrElse(maybe, () => possiblePaths[0])));
  return HttpRouter.empty.pipe(HttpRouter.get('/', HttpServerResponse.file(path.join(typesyncStudioClientDist, 'index.html')).pipe(Effect.orElse(() => HttpServerResponse.empty({
    status: 404
  })))),
  // specific handler for the /authenticate-callback endpoint for auth
  HttpRouter.get('/authenticate-callback', HttpServerResponse.file(path.join(typesyncStudioClientDist, 'index.html')).pipe(Effect.orElse(() => HttpServerResponse.empty({
    status: 404
  })))), HttpRouter.get('/assets/:file', Effect.gen(function* () {
    const file = yield* HttpRouter.params.pipe(Effect.map(Struct.get('file')), Effect.map(Option.fromNullable));
    if (Option.isNone(file)) {
      return HttpServerResponse.empty({
        status: 404
      });
    }
    const assets = path.join(typesyncStudioClientDist, 'assets');
    const normalized = path.normalize(path.join(assets, ...file.value.split('/')));
    if (!normalized.startsWith(assets)) {
      return HttpServerResponse.empty({
        status: 404
      });
    }
    return yield* HttpServerResponse.file(normalized);
  }).pipe(Effect.orElse(() => HttpServerResponse.empty({
    status: 404
  })))));
});
const Server = /*#__PURE__*/Effect.all({
  api: HypergraphTypeSyncApiLive,
  files: TypesyncStudioFileRouter
}).pipe(/*#__PURE__*/Effect.map(({
  api,
  files
}) => HttpRouter.empty.pipe(HttpRouter.mount('/', files), HttpRouter.mountApp('/api', api, {
  includePrefix: true
}))), /*#__PURE__*/Effect.map(router => HttpServer.serve(HttpMiddleware.logger)(router)), Layer.unwrapEffect);
const openBrowser = (port, browser) => Effect.async(resume => {
  const url = `http://localhost:${port}`;
  const launch = appOpts => open(url, appOpts ? {
    app: appOpts
  } : undefined).then(subprocess => {
    subprocess.on('spawn', () => resume(Effect.void));
    subprocess.on('error', err => resume(Effect.fail(new OpenBrowserError({
      cause: err
    }))));
  });
  const mapBrowserName = b => {
    switch (b) {
      case 'chrome':
        return apps.chrome;
      // cross-platform alias from open
      case 'firefox':
        return apps.firefox;
      case 'edge':
        return apps.edge;
      case 'safari':
        return 'Safari';
      case 'arc':
        return 'Arc';
      default:
        return undefined;
    }
  };
  switch (browser) {
    case 'browser':
      launch();
      break;
    case 'browserPrivate':
      launch({
        name: apps.browserPrivate
      });
      break;
    default:
      {
        const mapped = mapBrowserName(browser);
        if (mapped) {
          launch({
            name: mapped
          }).catch(() => launch());
          break;
        }
        launch();
        break;
      }
  }
});
export class OpenBrowserError extends /*#__PURE__*/Data.TaggedError('Nozzle/cli/studio/errors/OpenBrowserError') {}
export const typesync = /*#__PURE__*/Command.make('typesync', {
  args: {
    open: Options.boolean('open').pipe(Options.withDescription('If true, opens the nozzle dataset studio in your browser'), Options.withDefault(true)),
    browser: Options.choice('browser', ['chrome', 'firefox', 'edge', 'safari', 'arc', 'browser', 'browserPrivate']).pipe(Options.withAlias('b'), Options.withDescription('Broweser to open the nozzle dataset studio app in. Default is your default selected browser'), Options.withDefault('browser'))
  }
}).pipe(/*#__PURE__*/Command.withDescription('Opens the TypeSync studio to help users build and publish their Hypergraph application schema'), /*#__PURE__*/Command.withHandler(({
  args
}) => Effect.gen(function* () {
  yield* Server.pipe(HttpServer.withLogAddress, Layer.provide(NodeHttpServer.layer(createServer, {
    port: 3000
  })), Layer.tap(() => Effect.gen(function* () {
    if (args.open) {
      return yield* openBrowser(3000, args.browser).pipe(Effect.tapErrorCause(cause => Effect.logWarning(AnsiDoc.text('Failure opening nozzle dataset studio in your browser. Open at http://localhost:3000'), AnsiDoc.text(Cause.pretty(cause)))), Effect.orElseSucceed(() => Effect.void));
    }
    return Effect.void;
  })), Layer.tap(() => Effect.logInfo(AnsiDoc.text('🎉 TypeSync studio started and running at http://localhost:3000'))), Layer.launch);
})), /*#__PURE__*/Command.provide(Typesync.layer));
//# sourceMappingURL=typesync.js.map