import { EngagementEvent } from '@core/analytics/domain/entities/EngagementEvent';
import type { EngagementAction, EngagementEntityType } from '@core/analytics/domain/types/EngagementEvent';

import { EngagementEventOrmEntity } from '../entities/EngagementEventOrmEntity';
import { TypeOrmAnalyticsSchemaError } from '../errors/TypeOrmAnalyticsSchemaError';
import {
    assertDate,
    assertEnumValue,
    assertNonEmptyString,
    assertOptionalStringOrNull,
    assertRecord,
} from '../schema/TypeOrmAnalyticsSchemaGuards';

const VALID_ACTIONS: readonly EngagementAction[] = [
  'click_sponsor_logo',
  'click_sponsor_url',
  'download_livery_pack',
  'join_league',
  'register_race',
  'view_standings',
  'view_schedule',
  'share_social',
  'contact_sponsor',
] as const;

const VALID_ENTITY_TYPES: readonly EngagementEntityType[] = [
  'league',
  'driver',
  'team',
  'race',
  'sponsor',
  'sponsorship',
] as const;

const VALID_ACTOR_TYPES: readonly EngagementEvent['actorType'][] = ['anonymous', 'driver', 'sponsor'] as const;

function assertMetadataShape(
  entityName: string,
  fieldName: string,
  value: unknown,
): asserts value is Record<string, string | number | boolean> | null | undefined {
  if (value === undefined || value === null) return;

  assertRecord(entityName, fieldName, value);

  for (const [k, v] of Object.entries(value)) {
    const allowed =
      typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean' || v === undefined || v === null;
    if (!allowed) {
      throw new TypeOrmAnalyticsSchemaError({
        entityName,
        fieldName,
        reason: 'invalid_shape',
        message: `Invalid metadata value for key "${k}"`,
      });
    }
  }
}

export class EngagementEventOrmMapper {
  toOrmEntity(domain: EngagementEvent): EngagementEventOrmEntity {
    const entity = new EngagementEventOrmEntity();
    entity.id = domain.id;
    entity.action = domain.action;
    entity.entityType = domain.entityType;
    entity.entityId = domain.entityId;

    entity.actorId = domain.actorId ?? null;
    entity.actorType = domain.actorType;
    entity.sessionId = domain.sessionId;

    entity.metadata = domain.metadata ?? null;
    entity.timestamp = domain.timestamp;

    return entity;
  }

  toDomain(entity: EngagementEventOrmEntity): EngagementEvent {
    const entityName = 'AnalyticsEngagementEvent';

    try {
      assertNonEmptyString(entityName, 'id', entity.id);
      assertEnumValue<EngagementAction>(entityName, 'action', entity.action, VALID_ACTIONS);
      assertEnumValue<EngagementEntityType>(entityName, 'entityType', entity.entityType, VALID_ENTITY_TYPES);
      assertNonEmptyString(entityName, 'entityId', entity.entityId);

      assertOptionalStringOrNull(entityName, 'actorId', entity.actorId);
      assertEnumValue<EngagementEvent['actorType']>(entityName, 'actorType', entity.actorType, VALID_ACTOR_TYPES);

      assertNonEmptyString(entityName, 'sessionId', entity.sessionId);
      assertMetadataShape(entityName, 'metadata', entity.metadata);

      assertDate(entityName, 'timestamp', entity.timestamp);

      return EngagementEvent.create({
        id: entity.id,
        action: entity.action,
        entityType: entity.entityType,
        entityId: entity.entityId,
        actorType: entity.actorType,
        sessionId: entity.sessionId,
        timestamp: entity.timestamp,
        ...(entity.actorId !== null && entity.actorId !== undefined ? { actorId: entity.actorId } : {}),
        ...(entity.metadata !== null && entity.metadata !== undefined ? { metadata: entity.metadata } : {}),
      });
    } catch (error) {
      if (error instanceof TypeOrmAnalyticsSchemaError) {
        throw error;
      }

      const message = error instanceof Error ? error.message : 'Invalid persisted AnalyticsEngagementEvent';
      throw new TypeOrmAnalyticsSchemaError({ entityName, fieldName: 'unknown', reason: 'invalid_shape', message });
    }
  }
}