import { CampaignsRepository } from "../repositories/campaigns-repository";
import { CampaignLogsRepository } from "../repositories/campaign-logs-repository";
import { AnalyticsEmailEvent, EmailEventsRepository } from "@/modules/ses-webhooks/repositories/email-events-repository";

export type DashboardPeriod = "7D" | "30D" | "90D" | "CUSTOM";

export interface TimeSeriesDataPoint {
  label: string;
  envios: number;
  aberturas: number;
  cliques: number;
}

export interface DomainProviderReputation {
  provider: string;
  deliveredRate: number;
  deliveredText: string;
}

export interface DashboardAnalyticsResponse {
  period: DashboardPeriod;
  activeCampaigns: number;
  totalDispatches: number;
  totalDelivered: number;
  openRate: number;
  clickRate: number;
  healthRate: number;
  bouncesCount: number;
  timeSeriesData: TimeSeriesDataPoint[];
  providerReputations: DomainProviderReputation[];
}

interface GetDashboardAnalyticsRequest {
  userId: string;
  period?: DashboardPeriod;
}

export class GetDashboardAnalyticsUseCase {
  constructor(
    private campaignsRepository: CampaignsRepository,
    private campaignLogsRepository: CampaignLogsRepository,
    private emailEventsRepository: EmailEventsRepository
  ) {}

  async execute({
    userId,
    period = "30D",
  }: GetDashboardAnalyticsRequest): Promise<DashboardAnalyticsResponse> {
    const campaigns = await this.campaignsRepository.findByUserId(userId);
    const campaignIds = campaigns.map((c) => c.id);

    const now = new Date();
    let startDate: Date;

    if (period === "7D") {
      startDate = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
    } else if (period === "90D") {
      startDate = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1000);
    } else {
      // Default to 30D (or CUSTOM)
      startDate = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
    }

    const logs = await this.campaignLogsRepository.findLogsForAnalytics(
      campaignIds,
      startDate,
      now
    );
    const events = await this.emailEventsRepository.findEventsForAnalytics(
      campaignIds,
      startDate,
      now
    );

    // Active campaigns count in period or total campaigns
    const activeCampaigns = campaigns.filter(
      (c) => c.status === "SENDING" || c.status === "SENT" || c.status === "COMPLETED_WITH_ERRORS"
    ).length;

    const totalDispatches = logs.length;
    const totalDelivered = events.filter((e) => e.type === "DELIVERY").length;
    const bouncesCount = events.filter((e) => e.type === "BOUNCE").length;
    const opens = events.filter((e) => e.type === "OPEN").length;
    const clicks = events.filter((e) => e.type === "CLICK").length;

    const openRate = totalDispatches > 0 ? Number(((opens / totalDispatches) * 100).toFixed(1)) : 0;
    const clickRate = totalDispatches > 0 ? Number(((clicks / totalDispatches) * 100).toFixed(1)) : 0;
    const healthRate = totalDispatches > 0 ? Number((((totalDispatches - bouncesCount) / totalDispatches) * 100).toFixed(1)) : 100;

    // Time Series Generation
    const timeSeriesData = this.buildTimeSeriesData(logs, events, period, startDate, now);

    // Provider Reputations Calculation
    const providerReputations = this.buildProviderReputations(logs);

    return {
      period,
      activeCampaigns: activeCampaigns || campaigns.length,
      totalDispatches,
      totalDelivered,
      openRate,
      clickRate,
      healthRate,
      bouncesCount,
      timeSeriesData,
      providerReputations,
    };
  }

  private buildTimeSeriesData(
    logs: Array<{ status: string; createdAt: Date }>,
    events: AnalyticsEmailEvent[],
    period: DashboardPeriod,
    startDate: Date,
    now: Date
  ): TimeSeriesDataPoint[] {
    if (period === "7D") {
      const days = ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"];
      const points: TimeSeriesDataPoint[] = [];

      for (let i = 6; i >= 0; i--) {
        const d = new Date(now);
        d.setDate(d.getDate() - i);
        const dayName = days[d.getDay()];

        const envios = logs.filter(
          (l) => new Date(l.createdAt).toDateString() === d.toDateString()
        ).length;
        const dayEvents = events.filter(
          (e) => new Date(e.createdAt).toDateString() === d.toDateString()
        );
        const aberturas = dayEvents.filter((e) => e.type === "OPEN").length;
        const cliques = dayEvents.filter((e) => e.type === "CLICK").length;

        points.push({ label: dayName, envios, aberturas, cliques });
      }
      return points;
    }

    if (period === "90D") {
      const points: TimeSeriesDataPoint[] = [
        { label: "Mês 1 - Sem 1", envios: 0, aberturas: 0, cliques: 0 },
        { label: "Mês 1 - Sem 3", envios: 0, aberturas: 0, cliques: 0 },
        { label: "Mês 2 - Sem 1", envios: 0, aberturas: 0, cliques: 0 },
        { label: "Mês 2 - Sem 3", envios: 0, aberturas: 0, cliques: 0 },
        { label: "Mês 3 - Sem 1", envios: 0, aberturas: 0, cliques: 0 },
        { label: "Mês 3 - Sem 3", envios: 0, aberturas: 0, cliques: 0 },
      ];

      const intervalMs = (now.getTime() - startDate.getTime()) / 6;
      logs.forEach((log) => {
        const index = this.bucketIndex(log.createdAt, startDate, intervalMs, 5);
        points[index].envios += 1;
      });
      events.forEach((event) => {
        const index = this.bucketIndex(event.createdAt, startDate, intervalMs, 5);
        if (event.type === "OPEN") points[index].aberturas += 1;
        if (event.type === "CLICK") points[index].cliques += 1;
      });

      return points;
    }

    // Default to 30D (4 weeks)
    const points: TimeSeriesDataPoint[] = [
      { label: "Semana 1", envios: 0, aberturas: 0, cliques: 0 },
      { label: "Semana 2", envios: 0, aberturas: 0, cliques: 0 },
      { label: "Semana 3", envios: 0, aberturas: 0, cliques: 0 },
      { label: "Semana 4", envios: 0, aberturas: 0, cliques: 0 },
    ];

    const intervalMs = (now.getTime() - startDate.getTime()) / 4;
    logs.forEach((log) => {
      const index = this.bucketIndex(log.createdAt, startDate, intervalMs, 3);
      points[index].envios += 1;
    });
    events.forEach((event) => {
      const index = this.bucketIndex(event.createdAt, startDate, intervalMs, 3);
      if (event.type === "OPEN") points[index].aberturas += 1;
      if (event.type === "CLICK") points[index].cliques += 1;
    });

    return points;
  }

  private bucketIndex(date: Date, startDate: Date, intervalMs: number, maxIndex: number): number {
    const time = new Date(date).getTime();
    return Math.min(maxIndex, Math.max(0, Math.floor((time - startDate.getTime()) / intervalMs)));
  }

  private buildProviderReputations(
    logs: Array<{ status: string; contact: { email: string } }>
  ): DomainProviderReputation[] {
    const providers = {
      gmail: { name: "Gmail (Google)", total: 0, success: 0 },
      outlook: { name: "Outlook / Microsoft", total: 0, success: 0 },
      yahoo: { name: "Yahoo / AOL", total: 0, success: 0 },
      corp: { name: "Provedores Corporativos", total: 0, success: 0 },
    };

    logs.forEach((log) => {
      const email = log.contact.email.toLowerCase();
      const domain = email.split("@")[1] || "";

      let key: keyof typeof providers = "corp";
      if (domain.includes("gmail") || domain.includes("googlemail")) {
        key = "gmail";
      } else if (
        domain.includes("outlook") ||
        domain.includes("hotmail") ||
        domain.includes("live") ||
        domain.includes("msn")
      ) {
        key = "outlook";
      } else if (domain.includes("yahoo") || domain.includes("aol") || domain.includes("ymail")) {
        key = "yahoo";
      }

      providers[key].total += 1;
      if (log.status === "SUCCESS") {
        providers[key].success += 1;
      }
    });

    return Object.values(providers).map((p) => {
      const rate = p.total > 0 ? Number(((p.success / p.total) * 100).toFixed(1)) : 100.0;
      return {
        provider: p.name,
        deliveredRate: rate,
        deliveredText: `${rate}% entregue`,
      };
    });
  }
}
