import { Injectable } from '@nestjs/common';
import {
  SchedulerClient,
  CreateScheduleCommand,
  UpdateScheduleCommand,
  DeleteScheduleCommand,
  FlexibleTimeWindowMode,
  ActionAfterCompletion,
  ConflictException,
  ResourceNotFoundException,
  CreateScheduleCommandInput,
} from '@aws-sdk/client-scheduler';
import { AppLoggerService } from 'src/common/services/logger.service';
import { getAwsSchedulerClientConfig } from './config/aws-scheduler.config';
import { OneTimeScheduleParams } from './interfaces/aws-scheduler.interface';

/**
 * Generic wrapper over the EventBridge Scheduler API — no domain knowledge.
 * Throws on real AWS errors; only idempotency cases are silenced (exists→update,
 * missing→no-op).
 */
@Injectable()
export class AwsSchedulerService {
  private readonly client: SchedulerClient;

  constructor(private readonly logger: AppLoggerService) {
    this.client = new SchedulerClient(getAwsSchedulerClientConfig());
  }

  /** Create, or replace if the name already exists. Idempotent. */
  async upsertOneTimeSchedule(params: OneTimeScheduleParams): Promise<void> {
    const input = this.buildInput(params);
    try {
      await this.client.send(new CreateScheduleCommand(input));
    } catch (error) {
      if (error instanceof ConflictException) {
        await this.client.send(new UpdateScheduleCommand(input));
        return;
      }
      throw error;
    }
  }

  /** Delete a schedule by name. A missing schedule is treated as success. */
  async deleteSchedule(name: string, groupName?: string): Promise<void> {
    try {
      await this.client.send(new DeleteScheduleCommand({ Name: name, GroupName: groupName }));
    } catch (error) {
      if (error instanceof ResourceNotFoundException) return; // already gone / auto-deleted
      throw error;
    }
  }

  // Create and Update both take the full definition (Update is a replace).
  private buildInput(params: OneTimeScheduleParams): CreateScheduleCommandInput {
    const { name, groupName, target } = params;
    return {
      Name: name,
      GroupName: groupName,
      ScheduleExpression: this.toScheduleExpression(params.fireAtIso),
      ScheduleExpressionTimezone: 'UTC',
      FlexibleTimeWindow: { Mode: 'OFF' as FlexibleTimeWindowMode },
      ActionAfterCompletion: (params.deleteAfterCompletion === false
        ? 'NONE'
        : 'DELETE') as ActionAfterCompletion,
      State: 'ENABLED',
      Target: {
        Arn: target.arn,
        RoleArn: target.roleArn,
        Input: target.input,
        ...(target.retry
          ? {
              RetryPolicy: {
                MaximumRetryAttempts: target.retry.maxAttempts,
                MaximumEventAgeInSeconds: target.retry.maxEventAgeSeconds,
              },
            }
          : {}),
      },
    };
  }

  // ISO → `at(yyyy-MM-ddTHH:mm:ss)` in UTC (zone set via ScheduleExpressionTimezone).
  private toScheduleExpression(iso: string): string {
    const fireAt = new Date(iso);
    const pad = (value: number): string => String(value).padStart(2, '0');
    const utc =
      `${fireAt.getUTCFullYear()}-${pad(fireAt.getUTCMonth() + 1)}-${pad(fireAt.getUTCDate())}` +
      `T${pad(fireAt.getUTCHours())}:${pad(fireAt.getUTCMinutes())}:${pad(fireAt.getUTCSeconds())}`;
    return `at(${utc})`;
  }
}
