import { Injectable, inject } from '@angular/core';
import {
  type Message,
  type MessageRevision,
  type MessageRevisionType
} from '../../../../shared-kernel';
import { RealtimeSessionFacade } from '../../../../core/realtime';
import { DatabaseService } from '../../../../infrastructure/persistence';
import { MessageSigningService } from '../../../authentication/application/services/message-signing.service';
import { buildMessageRevision } from '../../domain/rules/message-revision.builder.rules';
import { getMessageRevision } from '../../domain/rules/message-integrity.rules';
import { attachRevisionSignatureIfPossible, shouldAcceptRevisionWithoutRegisteredKey } from '../../domain/rules/message-revision-signing.rules';

@Injectable({ providedIn: 'root' })
export class MessageRevisionService {
  private readonly db = inject(DatabaseService);
  private readonly signing = inject(MessageSigningService);
  private readonly realtime = inject(RealtimeSessionFacade);

  async createSignedRevision(input: {
    message: Message;
    type: MessageRevisionType;
    actorId: string;
    content?: string;
    editedAt: number;
    isDeleted?: boolean;
    pluginId?: string;
    sign?: boolean;
  }): Promise<MessageRevision> {
    const prevRevisionHash = input.type === 'create'
      ? ''
      : (input.message.headHash ?? '');

    let revision = await buildMessageRevision({
      message: input.message,
      type: input.type,
      actorId: input.actorId,
      content: input.content,
      editedAt: input.editedAt,
      isDeleted: input.isDeleted,
      pluginId: input.pluginId,
      prevRevisionHash
    });

    if (input.sign !== false) {
      revision = await attachRevisionSignatureIfPossible(
        revision,
        (value) => this.signing.signRevision(value)
      );
    }

    return revision;
  }

  async persistRevision(revision: MessageRevision): Promise<void> {
    await this.db.saveMessageRevision(revision);
  }

  broadcastRevision(revision: MessageRevision): void {
    this.realtime.broadcastMessage({
      type: 'message-revision',
      revision
    });
  }

  async persistAndBroadcast(revision: MessageRevision): Promise<void> {
    await this.persistRevision(revision);
    this.broadcastRevision(revision);
  }

  async buildCreateRevision(message: Message, actorId: string): Promise<MessageRevision> {
    return await this.createSignedRevision({
      message: {
        ...message,
        revision: 0
      },
      type: 'create',
      actorId,
      editedAt: message.timestamp
    });
  }

  resolveNextRevision(message: Message): number {
    return getMessageRevision(message) + 1;
  }

  async verifyRevision(revision: MessageRevision): Promise<boolean> {
    if (!revision.signature) {
      return true;
    }

    const publicKeyJwk = await this.signing.fetchSigningPublicKey(revision.actorId);

    if (shouldAcceptRevisionWithoutRegisteredKey(revision, publicKeyJwk)) {
      return true;
    }

    if (!publicKeyJwk) {
      return false;
    }

    return await this.signing.verifyRevisionSignature(revision, publicKeyJwk);
  }
}
