import { Injectable, Logger } from '@nestjs/common';
import { Visitor, VisitorEntity, VisitorEvent } from '../entities/Visitor';
import { v4 as uuidv4 } from 'uuid';
import knex from 'knex';
import { db } from '../../config/database';

@Injectable()
export class VisitorTrackingService {
  private readonly logger = new Logger(VisitorTrackingService.name);
  private knex: knex.Knex;

  constructor() {
    this.knex = db;
  }

  async trackVisit(
    ip: string,
    userAgent: string,
    referrer: string,
    page: string,
  ): Promise<string> {
    try {
      const visitorId = uuidv4();
      const visitor = VisitorEntity.create({
        id: visitorId,
        ipAddress: ip,
        userAgent,
        referrer,
        landingPage: page,
        pages: [page],
      });

      await this.knex('cf_visitors').insert({
        id: visitor.id,
        sessionId: visitor.sessionId,
        ipAddress: visitor.ipAddress,
        userAgent: visitor.userAgent,
        referrer: visitor.referrer,
        landingPage: visitor.landingPage,
        pages: JSON.stringify(visitor.pages),
        events: JSON.stringify(visitor.events),
        firstVisitAt: visitor.firstVisitAt,
        lastVisitAt: visitor.lastVisitAt,
        visitCount: visitor.visitCount,
        converted: visitor.converted,
        createdAt: visitor.createdAt,
        updatedAt: visitor.updatedAt,
      });

      this.logger.log(`Tracked visitor: ${visitorId} visiting ${page}`);
      return visitorId;
    } catch (error) {
      this.logger.error('Failed to track visitor', error);
      throw error;
    }
  }

  async getVisitorStats(
    startDate: Date,
    endDate: Date,
  ): Promise<{
    totalVisitors: number;
    uniqueVisitors: number;
    pageViews: number;
    referrers: Array<{ referrer: string; count: number }>;
    pages: Array<{ page: string; count: number }>;
  }> {
    try {
      const totalVisitors = await this.knex('cf_visitors')
        .whereBetween('firstVisitAt', [startDate, endDate])
        .count('* as count')
        .first();

      const uniqueVisitors = await this.knex('cf_visitors')
        .whereBetween('firstVisitAt', [startDate, endDate])
        .countDistinct('ipAddress as count')
        .first();

      const pageViews = await this.knex('cf_visitors')
        .whereBetween('firstVisitAt', [startDate, endDate])
        .count('* as count')
        .first();

      const referrers = await this.knex('cf_visitors')
        .whereBetween('firstVisitAt', [startDate, endDate])
        .whereNotNull('referrer')
        .select('referrer')
        .count('* as count')
        .groupBy('referrer')
        .orderBy('count', 'desc')
        .limit(10);

      const pages = await this.knex('cf_visitors')
        .whereBetween('firstVisitAt', [startDate, endDate])
        .select('landingPage as page')
        .count('* as count')
        .groupBy('landingPage')
        .orderBy('count', 'desc')
        .limit(10);

      return {
        totalVisitors: Number(totalVisitors?.count || 0),
        uniqueVisitors: Number(uniqueVisitors?.count || 0),
        pageViews: Number(pageViews?.count || 0),
        referrers: referrers.map((r) => ({ referrer: String(r.referrer || ''), count: Number(r.count) })),
        pages: pages.map((p) => ({ page: String(p.page || ''), count: Number(p.count) })),
      };
    } catch (error) {
      this.logger.error('Failed to get visitor stats', error);
      throw error;
    }
  }

  async cleanOldVisitors(days: number = 30): Promise<number> {
    try {
      const cutoffDate = new Date();
      cutoffDate.setDate(cutoffDate.getDate() - days);

      const deleted = await this.knex('cf_visitors')
        .where('firstVisitAt', '<', cutoffDate)
        .delete();

      this.logger.log(`Cleaned ${deleted} old visitor records`);
      return deleted;
    } catch (error) {
      this.logger.error('Failed to clean old visitors', error);
      throw error;
    }
  }
}
