import * as Option from 'effect/Option';
import * as Schema from 'effect/Schema';
import * as SchemaAST from 'effect/SchemaAST';
import { PropertyIdSymbol, PropertyTypeSymbol, RelationBacklinkSymbol, RelationPropertiesSymbol, RelationSchemaSymbol, RelationSymbol, TypeIdsSymbol } from '../constants.js';
export const relationSchemaBrand = '__hypergraphRelationSchema';
export const relationBuilderBrand = '__hypergraphRelationBuilder';
const hasRelationProperties = options => {
  if (!options) return false;
  return 'properties' in options;
};
/**
 * Creates a String schema with the specified GRC-20 property ID
 */
// biome-ignore lint/suspicious/noShadowRestrictedNames: is part of a namespaces module and therefor ok
export const String = propertyId => {
  return Schema.String.pipe(Schema.annotations({
    [PropertyIdSymbol]: propertyId,
    [PropertyTypeSymbol]: 'string'
  }));
};
/**
 * Creates a Number schema with the specified GRC-20 property ID
 */
// biome-ignore lint/suspicious/noShadowRestrictedNames: is part of a namespaces module and therefor ok
export const Number = propertyId => {
  return Schema.Number.pipe(Schema.annotations({
    [PropertyIdSymbol]: propertyId,
    [PropertyTypeSymbol]: 'number'
  }));
};
/**
 * Creates a Boolean schema with the specified GRC-20 property ID
 */
// biome-ignore lint/suspicious/noShadowRestrictedNames: is part of a namespaces module and therefor ok
export const Boolean = propertyId => {
  return Schema.Boolean.pipe(Schema.annotations({
    [PropertyIdSymbol]: propertyId,
    [PropertyTypeSymbol]: 'boolean'
  }));
};
/**
 * Creates a Date schema with the specified GRC-20 property ID
 */
// biome-ignore lint/suspicious/noShadowRestrictedNames: is part of a namespaces module and therefor ok
export const Date = propertyId => {
  return Schema.Date.pipe(Schema.annotations({
    [PropertyIdSymbol]: propertyId,
    [PropertyTypeSymbol]: 'date'
  }));
};
export const Point = propertyId => Schema.transform(Schema.String, Schema.Array(Schema.Number), {
  strict: true,
  decode: str => {
    return str.split(',').map(n => globalThis.Number(n));
  },
  encode: points => points.join(',')
}).pipe(Schema.annotations({
  [PropertyIdSymbol]: propertyId,
  [PropertyTypeSymbol]: 'point'
}));
export function Relation(schema, options) {
  return mapping => {
    const {
      propertyId,
      relationPropertyIds
    } = typeof mapping === 'string' ? {
      propertyId: mapping,
      relationPropertyIds: undefined
    } : typeof mapping === 'object' && mapping !== null && 'properties' in mapping ? {
      propertyId: mapping.propertyId,
      relationPropertyIds: mapping.properties
    } : {
      propertyId: mapping.propertyId,
      relationPropertyIds: undefined
    };
    const typeIds = SchemaAST.getAnnotation(TypeIdsSymbol)(schema.ast).pipe(Option.getOrElse(() => []));
    const relationEntityPropertiesSchemas = {};
    const normalizedOptions = options;
    const relationProperties = hasRelationProperties(normalizedOptions) ? normalizedOptions.properties : undefined;
    if (relationProperties) {
      for (const [key, schemaType] of Object.entries(relationProperties)) {
        const propertyMapping = relationPropertyIds?.[key];
        relationEntityPropertiesSchemas[key] = schemaType(propertyMapping);
      }
    }
    const relationEntitySchemaStruct = Schema.Struct({
      ...relationEntityPropertiesSchemas
    });
    const schemaWithId = Schema.asSchema(Schema.extend(schema)(Schema.Struct({
      id: Schema.String,
      _relation: Schema.extend(relationEntitySchemaStruct)(Schema.Struct({
        id: Schema.String
      }))
    }))).pipe(Schema.annotations({
      [TypeIdsSymbol]: typeIds,
      [RelationPropertiesSymbol]: relationEntitySchemaStruct
    }));
    const isBacklinkRelation = !!normalizedOptions?.backlink;
    const relationSchema = Schema.Array(schemaWithId).pipe(Schema.annotations({
      [PropertyIdSymbol]: propertyId,
      [RelationSchemaSymbol]: schema,
      [RelationSymbol]: true,
      [PropertyTypeSymbol]: 'relation',
      [RelationBacklinkSymbol]: isBacklinkRelation
    }));
    Object.defineProperty(relationSchema, relationSchemaBrand, {
      value: true,
      enumerable: false,
      configurable: false
    });
    return relationSchema;
  };
}
export function Backlink(schema, options) {
  const normalizedOptions = {
    ...(options ?? {}),
    backlink: true
  };
  return Relation(schema, normalizedOptions);
}
export const optional = schemaFn => propertyId => {
  const innerSchema = schemaFn(propertyId);
  const optionalSchema = Schema.optional(innerSchema);
  if (relationSchemaBrand in innerSchema) {
    Object.defineProperty(optionalSchema, relationSchemaBrand, {
      value: true,
      enumerable: false,
      configurable: false
    });
  }
  return optionalSchema;
};
//# sourceMappingURL=type.js.map