import { IssueStatus, IssueTypeName } from '@server/constants/issue';
import type { NotificationAgentNtfy } from '@server/lib/settings';
import { getSettings } from '@server/lib/settings';
import logger from '@server/logger';
import axios from 'axios';
import { Notification, hasNotificationType } from '..';
import type { NotificationAgent, NotificationPayload } from './agent';
import { BaseAgent } from './agent';

class NtfyAgent
  extends BaseAgent<NotificationAgentNtfy>
  implements NotificationAgent
{
  protected getSettings(): NotificationAgentNtfy {
    if (this.settings) {
      return this.settings;
    }

    const settings = getSettings();

    return settings.notifications.agents.ntfy;
  }

  private escapeMarkdown(text: string): string {
    return text.replace(/([\\`*_{}[\]()#+\-.!|>~<])/g, '\\$1');
  }

  private buildPayload(type: Notification, payload: NotificationPayload) {
    const settings = getSettings();
    const { applicationUrl } = settings.main;
    const { embedPoster } = settings.notifications.agents.ntfy;

    const topic = this.getSettings().options.topic;
    const priority = this.getSettings().options.priority ?? 3;

    const title = payload.event
      ? `${payload.event} - ${payload.subject}`
      : payload.subject;
    let message = payload.message ?? '';

    if (payload.request) {
      if (message) {
        message = `**Description:**\n${message}`;
      }
      message += `${message ? '\n\n' : ''}**Requested By:** ${this.escapeMarkdown(payload.request.requestedBy.displayName)}`;

      let status = '';
      switch (type) {
        case Notification.MEDIA_PENDING:
          status = 'Pending Approval';
          break;
        case Notification.MEDIA_APPROVED:
        case Notification.MEDIA_AUTO_APPROVED:
          status = 'Processing';
          break;
        case Notification.MEDIA_AVAILABLE:
          status = 'Available';
          break;
        case Notification.MEDIA_DECLINED:
          status = 'Declined';
          break;
        case Notification.MEDIA_FAILED:
          status = 'Failed';
          break;
      }

      if (status) {
        message += `\n**Request Status:** ${status}`;
      }
    } else if (payload.comment) {
      if (message) {
        message = `**Description:**\n${message}\n\n`;
      }
      message += `**Comment:**\n${payload.comment.message}`;
      message += `\n\n**Comment from:** ${this.escapeMarkdown(payload.comment.user.displayName)}`;
    } else if (payload.issue) {
      if (message) {
        message = `**Description:**\n${message}`;
      }
      message += `${message ? '\n\n' : ''}**Reported By:** ${this.escapeMarkdown(payload.issue.createdBy.displayName)}`;
      message += `\n**Issue Type:** ${IssueTypeName[payload.issue.issueType]}`;
      message += `\n**Issue Status:** ${
        payload.issue.status === IssueStatus.OPEN ? 'Open' : 'Resolved'
      }`;
    }

    for (const extra of payload.extra ?? []) {
      message += `\n\n**${extra.name}**\n${extra.value}`;
    }

    const attach = embedPoster ? payload.image : undefined;

    let click;
    if (applicationUrl && payload.media) {
      click = `${applicationUrl}/${payload.media.mediaType}/${payload.media.tmdbId}`;
    }

    return {
      topic,
      priority,
      title,
      message,
      markdown: true,
      attach,
      click,
    };
  }

  public shouldSend(): boolean {
    const settings = this.getSettings();

    if (settings.enabled && settings.options.url && settings.options.topic) {
      return true;
    }

    return false;
  }

  public async send(
    type: Notification,
    payload: NotificationPayload
  ): Promise<boolean> {
    const settings = this.getSettings();

    if (
      !payload.notifySystem ||
      !hasNotificationType(type, settings.types ?? 0)
    ) {
      return true;
    }

    logger.debug('Sending ntfy notification', {
      label: 'Notifications',
      type: Notification[type],
      subject: payload.subject,
    });

    try {
      let authHeader;
      if (
        settings.options.authMethodUsernamePassword &&
        settings.options.username &&
        settings.options.password
      ) {
        const encodedAuth = Buffer.from(
          `${settings.options.username}:${settings.options.password}`
        ).toString('base64');

        authHeader = `Basic ${encodedAuth}`;
      } else if (settings.options.authMethodToken) {
        authHeader = `Bearer ${settings.options.token}`;
      }

      await axios.post(
        settings.options.url,
        this.buildPayload(type, payload),
        authHeader
          ? {
              headers: {
                Authorization: authHeader,
              },
            }
          : undefined
      );

      return true;
    } catch (e) {
      logger.error('Error sending ntfy notification', {
        label: 'Notifications',
        type: Notification[type],
        subject: payload.subject,
        errorMessage: e.message,
        response: e?.response?.data,
      });

      return false;
    }
  }
}

export default NtfyAgent;
