import { settingsStore } from '../config/state';
import { logger } from '../utils/logger';
import { Client, EmbedBuilder, TextChannel } from 'discord.js';
import net from 'net';

export type StatusServiceType = 'http' | 'ping' | 'tcp' | 'custom' | 'unknown';

export interface StatusService {
  id: string;
  name: string;
  type?: StatusServiceType;
  target: string;
  description?: string;
  enabled?: boolean;
  status?: 'up' | 'down' | 'unknown';
  lastChecked?: number;
  upChecks?: number;
  totalChecks?: number;
}

export interface StatuspageConfig {
  enabled?: boolean;
  intervalMs?: number;
  services?: StatusService[];
  statusChannelId?: string;
  statusMessageId?: string;
}

export class StatuspageService {
  private timers = new Map<string, NodeJS.Timeout>();
  private client: Client | null = null;
  private fetcher: any = (globalThis as any).fetch;

  public setClient(client: Client) {
    this.client = client;
  }

  public async getConfig(guildId: string): Promise<StatuspageConfig> {
    const cfg = settingsStore.get(guildId) || {};
    const sp = (cfg as any).statuspageConfig || {};
    return {
      enabled: cfg.statuspageEnabled ?? sp.enabled ?? true,
      intervalMs: sp.intervalMs ?? 60000,
      services: sp.services ?? [],
      statusChannelId: sp.statusChannelId,
      statusMessageId: sp.statusMessageId
    };
  }

  public async saveConfig(guildId: string, cfg: StatuspageConfig) {
    await settingsStore.set(guildId, {
      statuspageEnabled: cfg.enabled,
      statuspageConfig: cfg
    } as any);
    this.restartTimer(guildId, cfg);
  }

  public async addService(guildId: string, service: Omit<StatusService, 'id'>) {
    const cfg = await this.getConfig(guildId);
    const id = `svc_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;
    const svc: StatusService = {
      id,
      name: service.name,
      type: service.type || 'unknown',
      target: service.target,
      description: service.description,
      enabled: service.enabled ?? true,
      status: 'unknown',
      upChecks: 0,
      totalChecks: 0
    };
    cfg.services = [...(cfg.services ?? []), svc];
    await this.saveConfig(guildId, cfg);
    return svc;
  }

  public async updateService(guildId: string, id: string, patch: Partial<StatusService>) {
    const cfg = await this.getConfig(guildId);
    cfg.services = (cfg.services ?? []).map((s) => (s.id === id ? { ...s, ...patch } : s));
    await this.saveConfig(guildId, cfg);
  }

  public async deleteService(guildId: string, id: string) {
    const cfg = await this.getConfig(guildId);
    cfg.services = (cfg.services ?? []).filter((s) => s.id !== id);
    await this.saveConfig(guildId, cfg);
  }

  public async getStatus(guildId: string) {
    const cfg = await this.getConfig(guildId);
    return {
      enabled: cfg.enabled !== false,
      services: cfg.services ?? []
    };
  }

  public async runChecks(guildId: string) {
    const cfg = await this.getConfig(guildId);
    if (cfg.enabled === false) return;
    const services = cfg.services ?? [];
    const updated = await Promise.all(
      services.map(async (svc) => {
        if (svc.enabled === false) return svc;
        const result = await this.checkService(svc);
        return result;
      })
    );
    cfg.services = updated;
    await settingsStore.set(guildId, { statuspageEnabled: cfg.enabled, statuspageConfig: cfg } as any);
    if (cfg.statusChannelId) {
      const messageId = await this.publishStatus(guildId, cfg.statusChannelId, cfg);
      if (messageId && cfg.statusMessageId !== messageId) {
        cfg.statusMessageId = messageId;
        await settingsStore.set(guildId, { statuspageConfig: cfg } as any);
      }
    }
  }

  private async checkService(svc: StatusService): Promise<StatusService> {
    const next = { ...svc };
    next.totalChecks = (next.totalChecks ?? 0) + 1;
    next.lastChecked = Date.now();
    try {
      if (!this.fetcher) {
        next.status = 'unknown';
        return next;
      }
      const type = (svc.type || 'unknown').toLowerCase();
      let target = svc.target || '';
      if (!target) {
        next.status = 'unknown';
        return next;
      }
      if ((type === 'http' || type === 'https') && !/^https?:\/\//i.test(target)) {
        target = 'http://' + target;
      }
      if (type === 'http' || type === 'https' || /^https?:\/\//i.test(target)) {
        const controller = new AbortController();
        const t = setTimeout(() => controller.abort(), 8000);
        const res = await this.fetcher(target, { method: 'GET', signal: controller.signal });
        clearTimeout(t);
        if (res.ok) {
          next.status = 'up';
          next.upChecks = (next.upChecks ?? 0) + 1;
        } else {
          next.status = 'down';
        }
      } else if (type === 'tcp' || type === 'ping') {
        const [host, portRaw] = target.split(':');
        const port = Number(portRaw) || 80;
        const ok = await this.checkTcp(host, port);
        if (ok) {
          next.status = 'up';
          next.upChecks = (next.upChecks ?? 0) + 1;
        } else {
          next.status = 'down';
        }
      } else {
        next.status = 'unknown';
      }
    } catch {
      next.status = 'down';
    }
    return next;
  }

  private restartTimer(guildId: string, cfg: StatuspageConfig) {
    const existing = this.timers.get(guildId);
    if (existing) clearInterval(existing);
    if (cfg.enabled === false) return;
    const interval = Math.max(30000, cfg.intervalMs ?? 60000);
    const timer = setInterval(() => this.runChecks(guildId).catch((err) => logger.warn(`status checks failed ${guildId}: ${err}`)), interval);
    this.timers.set(guildId, timer);
  }

  public async publishStatus(guildId: string, channelId: string, cfg?: StatuspageConfig) {
    if (!this.client) return;
    const channel = await this.client.channels.fetch(channelId).catch(() => null);
    if (!channel || !channel.isTextBased()) return;
    const current = cfg ?? (await this.getConfig(guildId));
    const services = current.services ?? [];
    const embed = new EmbedBuilder()
      .setTitle('Service Status')
      .setColor(services.some((s) => s.status === 'down') ? 0xef4444 : 0x22c55e)
      .setTimestamp(new Date());
    const lines = services.map((s) => {
      const icon = s.status === 'up' ? '✅' : s.status === 'down' ? '❌' : '⚪';
      const upPct =
        s.upChecks && s.totalChecks
          ? Math.round(((s.upChecks ?? 0) / Math.max(1, s.totalChecks ?? 1)) * 100)
          : 0;
      const last = s.lastChecked ? new Date(s.lastChecked).toLocaleString() : 'n/a';
      return `${icon} ${s.name} — ${upPct}% (${last})`;
    });
    embed.setDescription(lines.join('\n') || 'Keine Services konfiguriert.');
    let messageId = current.statusMessageId;
    try {
      if (messageId) {
        const msg = await (channel as TextChannel).messages.fetch(messageId);
        await msg.edit({ embeds: [embed] });
      } else {
        const sent = await (channel as TextChannel).send({ embeds: [embed] });
        messageId = sent.id;
      }
    } catch {
      const sent = await (channel as TextChannel).send({ embeds: [embed] });
      messageId = sent.id;
    }
    return messageId;
  }

  private checkTcp(host: string, port: number) {
    return new Promise<boolean>((resolve) => {
      const socket = new net.Socket();
      const done = (ok: boolean) => {
        socket.destroy();
        resolve(ok);
      };
      socket.setTimeout(6000);
      socket.once('error', () => done(false));
      socket.once('timeout', () => done(false));
      socket.connect(port, host, () => done(true));
    });
  }
}
