import { MediaReference } from '@core/domain/media/MediaReference';
import { League, type LeagueSettings } from '@core/racing/domain/entities/League';

import { LeagueOrmEntity } from '../entities/LeagueOrmEntity';
import { TypeOrmPersistenceSchemaError } from '../errors/TypeOrmPersistenceSchemaError';
import { assertEnumValue, assertNumber, assertRecord } from '../schema/TypeOrmSchemaGuards';
import type { SerializedLeagueSettings } from '../serialized/RacingTypeOrmSerialized';

const VALID_POINTS_SYSTEMS = ['f1-2024', 'indycar', 'custom'] as const;

const VALID_VISIBILITY = ['ranked', 'unranked'] as const;

const VALID_DECISION_MODES = [
  'admin_only',
  'steward_decides',
  'steward_vote',
  'member_vote',
  'steward_veto',
  'member_veto',
] as const;

function assertSerializedLeagueSettings(value: unknown): asserts value is SerializedLeagueSettings {
  const entityName = 'League';

  assertRecord(entityName, 'settings', value);

  const pointsSystem = value.pointsSystem;
  assertEnumValue(entityName, 'settings.pointsSystem', pointsSystem, VALID_POINTS_SYSTEMS);

  if (value.sessionDuration !== undefined) {
    assertNumber(entityName, 'settings.sessionDuration', value.sessionDuration);
  }

  if (value.qualifyingFormat !== undefined && typeof value.qualifyingFormat !== 'string') {
    throw new TypeOrmPersistenceSchemaError({
      entityName,
      fieldName: 'settings.qualifyingFormat',
      reason: 'not_string',
    });
  }

  if (value.maxDrivers !== undefined) {
    assertNumber(entityName, 'settings.maxDrivers', value.maxDrivers);
  }

  if (value.visibility !== undefined) {
    const visibility = value.visibility;
    assertEnumValue(entityName, 'settings.visibility', visibility, VALID_VISIBILITY);
  }

  if (value.stewarding !== undefined) {
    const stewarding = value.stewarding;
    assertRecord(entityName, 'settings.stewarding', stewarding);

    const decisionMode = stewarding.decisionMode;
    assertEnumValue(entityName, 'settings.stewarding.decisionMode', decisionMode, VALID_DECISION_MODES);

    if (stewarding.requiredVotes !== undefined) {
      assertNumber(entityName, 'settings.stewarding.requiredVotes', stewarding.requiredVotes);
    }
    if (stewarding.requireDefense !== undefined && typeof stewarding.requireDefense !== 'boolean') {
      throw new TypeOrmPersistenceSchemaError({
        entityName,
        fieldName: 'settings.stewarding.requireDefense',
        reason: 'not_boolean',
      });
    }
    if (stewarding.defenseTimeLimit !== undefined) {
      assertNumber(entityName, 'settings.stewarding.defenseTimeLimit', stewarding.defenseTimeLimit);
    }
    if (stewarding.voteTimeLimit !== undefined) {
      assertNumber(entityName, 'settings.stewarding.voteTimeLimit', stewarding.voteTimeLimit);
    }
    if (stewarding.protestDeadlineHours !== undefined) {
      assertNumber(entityName, 'settings.stewarding.protestDeadlineHours', stewarding.protestDeadlineHours);
    }
    if (stewarding.stewardingClosesHours !== undefined) {
      assertNumber(entityName, 'settings.stewarding.stewardingClosesHours', stewarding.stewardingClosesHours);
    }
    if (stewarding.notifyAccusedOnProtest !== undefined && typeof stewarding.notifyAccusedOnProtest !== 'boolean') {
      throw new TypeOrmPersistenceSchemaError({
        entityName,
        fieldName: 'settings.stewarding.notifyAccusedOnProtest',
        reason: 'not_boolean',
      });
    }
    if (stewarding.notifyOnVoteRequired !== undefined && typeof stewarding.notifyOnVoteRequired !== 'boolean') {
      throw new TypeOrmPersistenceSchemaError({
        entityName,
        fieldName: 'settings.stewarding.notifyOnVoteRequired',
        reason: 'not_boolean',
      });
    }
  }

  if (value.customPoints !== undefined) {
    const customPoints = value.customPoints;
    assertRecord(entityName, 'settings.customPoints', customPoints);

    for (const [key, points] of Object.entries(customPoints)) {
      if (!Number.isInteger(Number(key))) {
        throw new TypeOrmPersistenceSchemaError({
          entityName,
          fieldName: 'settings.customPoints',
          reason: 'invalid_shape',
          message: 'Invalid settings.customPoints (expected numeric keys)',
        });
      }
      assertNumber(entityName, `settings.customPoints.${key}`, points);
    }
  }
}

function parseLeagueSettings(value: unknown): LeagueSettings {
  assertSerializedLeagueSettings(value);

  const customPoints: Record<number, number> | undefined = value.customPoints
    ? Object.fromEntries(Object.entries(value.customPoints).map(([position, points]) => [Number(position), points]))
    : undefined;

  return {
    pointsSystem: value.pointsSystem,
    ...(value.sessionDuration !== undefined ? { sessionDuration: value.sessionDuration } : {}),
    ...(value.qualifyingFormat !== undefined ? { qualifyingFormat: value.qualifyingFormat } : {}),
    ...(customPoints !== undefined ? { customPoints } : {}),
    ...(value.maxDrivers !== undefined ? { maxDrivers: value.maxDrivers } : {}),
    ...(value.stewarding !== undefined ? { stewarding: value.stewarding } : {}),
    ...(value.visibility !== undefined ? { visibility: value.visibility } : {}),
  };
}

function serializeLeagueSettings(settings: LeagueSettings): SerializedLeagueSettings {
  const customPoints =
    settings.customPoints !== undefined
      ? Object.fromEntries(
          Object.entries(settings.customPoints).map(([position, points]) => [String(position), points]),
        )
      : undefined;

  return {
    pointsSystem: settings.pointsSystem,
    ...(settings.sessionDuration !== undefined ? { sessionDuration: settings.sessionDuration } : {}),
    ...(settings.qualifyingFormat !== undefined ? { qualifyingFormat: settings.qualifyingFormat } : {}),
    ...(customPoints !== undefined ? { customPoints } : {}),
    ...(settings.maxDrivers !== undefined ? { maxDrivers: settings.maxDrivers } : {}),
    ...(settings.stewarding !== undefined ? { stewarding: settings.stewarding } : {}),
    ...(settings.visibility !== undefined ? { visibility: settings.visibility } : {}),
  };
}

export class LeagueOrmMapper {
  toOrmEntity(domain: League): LeagueOrmEntity {
    const entity = new LeagueOrmEntity();
    entity.id = domain.id.toString();
    entity.name = domain.name.toString();
    entity.description = domain.description.toString();
    entity.ownerId = domain.ownerId.toString();
    entity.settings = serializeLeagueSettings(domain.settings);
    entity.category = domain.category ?? null;
    entity.createdAt = domain.createdAt.toDate();
    entity.participantCount = domain.getParticipantCount();
    entity.discordUrl = domain.socialLinks?.discordUrl ?? null;
    entity.youtubeUrl = domain.socialLinks?.youtubeUrl ?? null;
    entity.websiteUrl = domain.socialLinks?.websiteUrl ?? null;
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    entity.logoRef = domain.logoRef.toJSON() as any;
    return entity;
  }

  toDomain(entity: LeagueOrmEntity): League {
    const settings = parseLeagueSettings(entity.settings);

    return League.rehydrate({
      id: entity.id,
      name: entity.name,
      description: entity.description,
      ownerId: entity.ownerId,
      settings,
      category: entity.category ?? undefined,
      createdAt: entity.createdAt,
      participantCount: entity.participantCount,
      ...(entity.discordUrl || entity.youtubeUrl || entity.websiteUrl
        ? {
            socialLinks: {
              ...(entity.discordUrl ? { discordUrl: entity.discordUrl } : {}),
              ...(entity.youtubeUrl ? { youtubeUrl: entity.youtubeUrl } : {}),
              ...(entity.websiteUrl ? { websiteUrl: entity.websiteUrl } : {}),
            },
          }
        : {}),
      ...(entity.logoRef !== null && entity.logoRef !== undefined
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        ? { logoRef: MediaReference.fromJSON(entity.logoRef as any) }
        : {}),
    });
  }
}