import { createHash } from 'crypto';
import { prisma } from '../../config/database';
import { logger } from '../../utils/logger';
import { geoipService } from '../../services/geoip.service';

interface PageViewData {
  path: string;
  referrer?: string;
  sessionHash?: string;
  userAgent?: string;
  ipAddress?: string;
}

interface TopPage {
  path: string;
  views: number;
  uniqueSessions: number;
}

interface DayViews {
  date: string;
  views: number;
  uniqueSessions: number;
}

interface TopReferrer {
  referrer: string;
  count: number;
}

interface CountryViews {
  country: string;
  views: number;
}

interface GeoPoint {
  latitude: number;
  longitude: number;
  count: number;
}

interface AnalyticsSummary {
  totalViews: number;
  uniqueSessions: number;
  topPages: TopPage[];
  viewsByDay: DayViews[];
  topReferrers: TopReferrer[];
  viewsByCountry: CountryViews[];
  geoPoints: GeoPoint[];
}

type UniqueCountRow = { count: bigint };
type TopPageRow = { path: string; views: bigint; unique_sessions: bigint };
type DayViewRow = { day: Date; views: bigint; unique_sessions: bigint };
type ReferrerRow = { referrer: string; count: bigint };
type CountryRow = { country: string; views: bigint };
type GeoPointRow = { latitude: number; longitude: number; count: bigint };

export const docsAnalyticsService = {
  async recordPageView(data: PageViewData): Promise<void> {
    const geo = data.ipAddress ? await geoipService.lookup(data.ipAddress) : null;
    const ipAddressHash = data.ipAddress
      ? createHash('sha256').update(data.ipAddress).digest('hex')
      : null;

    await prisma.docsPageView.create({
      data: {
        path: data.path,
        referrer: data.referrer || null,
        sessionHash: data.sessionHash || null,
        userAgent: data.userAgent || null,
        ipAddressHash,
        country: geo?.country ?? null,
        region: geo?.region ?? null,
        city: geo?.city ?? null,
        latitude: geo?.latitude ?? null,
        longitude: geo?.longitude ?? null,
      },
    });
  },

  async getSummary(days: number): Promise<AnalyticsSummary> {
    const since = new Date();
    since.setDate(since.getDate() - days);

    const totalViewsP = prisma.docsPageView.count({
      where: { createdAt: { gte: since } },
    });

    const uniqueSessionsP = prisma.$queryRaw<UniqueCountRow[]>`
      SELECT COUNT(DISTINCT "sessionHash") as count
      FROM docs_page_views
      WHERE "createdAt" >= ${since}
        AND "sessionHash" IS NOT NULL
    `;

    const topPagesP = prisma.$queryRaw<TopPageRow[]>`
      SELECT path,
             COUNT(*) as views,
             COUNT(DISTINCT "sessionHash") as unique_sessions
      FROM docs_page_views
      WHERE "createdAt" >= ${since}
      GROUP BY path
      ORDER BY views DESC
      LIMIT 20
    `;

    const viewsByDayP = prisma.$queryRaw<DayViewRow[]>`
      SELECT DATE("createdAt") as day,
             COUNT(*) as views,
             COUNT(DISTINCT "sessionHash") as unique_sessions
      FROM docs_page_views
      WHERE "createdAt" >= ${since}
      GROUP BY DATE("createdAt")
      ORDER BY day ASC
    `;

    const topReferrersP = prisma.$queryRaw<ReferrerRow[]>`
      SELECT referrer,
             COUNT(*) as count
      FROM docs_page_views
      WHERE "createdAt" >= ${since}
        AND referrer IS NOT NULL
        AND referrer != ''
      GROUP BY referrer
      ORDER BY count DESC
      LIMIT 10
    `;

    const viewsByCountryP = prisma.$queryRaw<CountryRow[]>`
      SELECT country,
             COUNT(*) as views
      FROM docs_page_views
      WHERE "createdAt" >= ${since}
        AND country IS NOT NULL
      GROUP BY country
      ORDER BY views DESC
      LIMIT 20
    `;

    const geoPointsP = prisma.$queryRaw<GeoPointRow[]>`
      SELECT latitude, longitude, COUNT(*) as count
      FROM docs_page_views
      WHERE "createdAt" >= ${since}
        AND latitude IS NOT NULL
      GROUP BY latitude, longitude
      ORDER BY count DESC
      LIMIT 500
    `;

    const [totalViews, uniqueSessionsResult, topPagesRaw, viewsByDayRaw, topReferrersRaw, viewsByCountryRaw, geoPointsRaw] =
      await Promise.all([totalViewsP, uniqueSessionsP, topPagesP, viewsByDayP, topReferrersP, viewsByCountryP, geoPointsP]);

    return {
      totalViews,
      uniqueSessions: Number(uniqueSessionsResult[0]?.count ?? 0),
      topPages: topPagesRaw.map((r) => ({
        path: r.path,
        views: Number(r.views),
        uniqueSessions: Number(r.unique_sessions),
      })),
      viewsByDay: viewsByDayRaw.map((r) => ({
        date: r.day instanceof Date
          ? r.day.toISOString().split('T')[0]
          : String(r.day),
        views: Number(r.views),
        uniqueSessions: Number(r.unique_sessions),
      })),
      topReferrers: topReferrersRaw.map((r) => ({
        referrer: r.referrer,
        count: Number(r.count),
      })),
      viewsByCountry: viewsByCountryRaw.map((r) => ({
        country: r.country,
        views: Number(r.views),
      })),
      geoPoints: geoPointsRaw.map((r) => ({
        latitude: r.latitude,
        longitude: r.longitude,
        count: Number(r.count),
      })),
    };
  },

  async cleanupOldData(retentionDays = 90): Promise<number> {
    const cutoff = new Date();
    cutoff.setDate(cutoff.getDate() - retentionDays);

    const { count } = await prisma.docsPageView.deleteMany({
      where: { createdAt: { lt: cutoff } },
    });

    if (count > 0) {
      logger.info(`Cleaned up ${count} docs page views older than ${retentionDays} days`);
    }

    return count;
  },
};
