import { Injectable, Logger, HttpException, HttpStatus } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { ConfigService } from '@nestjs/config';
import { firstValueFrom, catchError } from 'rxjs';
import { AxiosResponse, AxiosError } from 'axios';
import { FetchMetaInsightsDto } from '../dto/fetch-insights.dto';
import { NormalizedCampaignInsight } from '../interfaces/campaign-insight.interface';
import { MetaNormalizer } from '../utils/normalize.util';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from '../../users/entities/user.entity';
import { ConnectedAccount } from '../entities/connected-account.entity';
import { ConnectAccountDto } from '../dto/connect-account.dto';
import { MetaCampaign } from '../entities/meta-campaign.entity';
import { AIAnalysis } from '../../analytics/entities/ai-analysis.entity';

/**
 * Meta Ads Service
 * خدمة إعلانات ميتا
 * 
 * Handles all direct communication with the Meta Marketing API.
 * يتعامل مع جميع الاتصالات المباشرة مع واجهة ميتا للتسويق.
 */
@Injectable()
export class MetaAdsService {
  private readonly logger = new Logger(MetaAdsService.name);
  private readonly accessToken: string;
  private readonly apiVersion: string;

  constructor(
    private readonly httpService: HttpService,
    private readonly configService: ConfigService,
    @InjectRepository(User)
    private readonly userRepository: Repository<User>,
    @InjectRepository(ConnectedAccount)
    private readonly accountRepository: Repository<ConnectedAccount>,
    @InjectRepository(MetaCampaign)
    private readonly campaignRepository: Repository<MetaCampaign>,
    @InjectRepository(AIAnalysis)
    private readonly aiAnalysisRepository: Repository<AIAnalysis>,
  ) {
    this.accessToken = this.configService.get<string>('meta.accessToken')!;
    this.apiVersion = this.configService.get<string>('meta.apiVersion') || 'v19.0';

    this.logger.log(`MetaAdsService Initialized | API Version: ${this.apiVersion}`);
  }

  /**
   * Fetch campaign insights from Meta API
   * جلب رؤى الحملة من واجهة ميتا
   */
  async fetchInsights(dto: FetchMetaInsightsDto): Promise<NormalizedCampaignInsight[]> {
    const finalAdAccountId = dto.adAccountId || this.configService.get<string>('meta.adAccountId');
    const dateStart = dto.dateStart || '2026-01-01'; // Get data for the current year by default
    const dateEnd = dto.dateEnd || new Date().toISOString().split('T')[0];
    const { level } = dto;

    if (!finalAdAccountId) {
      throw new HttpException('Ad Account ID is required', HttpStatus.BAD_REQUEST);
    }

    // Determine access token to use | تحديد توكن الوصول للاستخدام
    let accessToken = this.configService.get<string>('meta.accessToken');

    // Prioritize user-specific tokens if userId is provided
    // إعطاء الأولوية للتوكنات الخاصة بالمستخدم إذا تم توفير معرف المستخدم
    if (dto.userId) {
      const query: any = { user: { id: dto.userId }, platform: 'meta' };
      if (dto.adAccountId) {
        query.externalAdAccountId = dto.adAccountId;
      }

      const savedAccount = await this.accountRepository.findOne({
        where: query,
        order: { createdAt: 'DESC' }
      });

      if (savedAccount && savedAccount.accessToken) {
        accessToken = savedAccount.accessToken;
        this.logger.log(`Using saved ${savedAccount.platform} token for user ${dto.userId}`);

        // If the request didn't specify an account, use the one we found
        if (!dto.adAccountId) {
          dto.adAccountId = savedAccount.externalAdAccountId;
        }
      }
    }


    // Construct Meta Insights URL | بناء رابط رؤى ميتا
    // Format: /v19.0/act_<ID>/insights
    const url = `https://graph.facebook.com/${this.apiVersion}/${finalAdAccountId}/insights`;

    const params: any = {
      access_token: accessToken,
      level: level,
      time_range: JSON.stringify({ since: dateStart, until: dateEnd }),
      fields: 'campaign_id,campaign_name,impressions,clicks,spend,date_start,date_stop,reach,frequency,objective,conversions,cost_per_result',
      breakdowns: 'publisher_platform',
    };

    try {
      this.logger.log(`Fetching Meta insights for ${finalAdAccountId} from ${dateStart} to ${dateEnd}`);

      const response = await firstValueFrom(
        this.httpService.get(url, { params }),
      );

      const rawData = response.data.data || [];
      this.logger.log(`Received ${rawData.length} raw insight records from Meta`);

      // 1. Fetch All Campaigns for Status (Batch) | جلب حالات جميع الحملات (جماعي)
      let campaignStatuses: Record<string, string> = {};
      try {
        const campaignsRes = await firstValueFrom(this.httpService.get(`https://graph.facebook.com/${this.apiVersion}/${finalAdAccountId}/campaigns`, {
          params: { access_token: accessToken, fields: 'id,effective_status', limit: 200 }
        }));
        (campaignsRes.data.data || []).forEach((c: any) => {
          campaignStatuses[c.id] = c.effective_status;
        });
        this.logger.log(`Fetched status for ${Object.keys(campaignStatuses).length} campaigns.`);
      } catch (e) {
        this.logger.warn(`Failed batch status fetch: ${e.message}`);
      }

      // 2. Fetch All Ads for Visuals (Batch) | جلب جميع الإعلانات للتصاميم (جماعي)
      const campaignCreatives: Record<string, { imageUrl: string; adCopy: string; platform: string }> = {};
      try {
        const adsRes = await firstValueFrom(
          this.httpService.get(`https://graph.facebook.com/${this.apiVersion}/${finalAdAccountId}/ads`, {
            params: {
              access_token: accessToken,
              fields: 'campaign_id,adcreatives{image_url,thumbnail_url,body,object_story_spec,instagram_actor_id}',
              limit: 200,
            },
          }),
        );
        (adsRes.data.data || []).forEach((ad: any) => {
          if (!campaignCreatives[ad.campaign_id] && ad.adcreatives?.data?.length > 0) {
            const creative = ad.adcreatives.data[0];
            campaignCreatives[ad.campaign_id] = {
              imageUrl: creative.image_url || creative.thumbnail_url || '',
              adCopy:
                creative.body ||
                creative.object_story_spec?.link_data?.message ||
                creative.object_story_spec?.video_data?.message ||
                '',
              platform: creative.instagram_actor_id ? 'instagram' : 'facebook',
            };
          }
        });
        this.logger.log(`Fetched visuals for ${Object.keys(campaignCreatives).length} campaigns.`);
      } catch (e) {
        this.logger.warn(`Failed batch visuals fetch: ${e.message}`);
      }

      // 3. Map everything to insights | ربط جميع البيانات بنتائج الأداء
      const enrichedData = await Promise.all(rawData.map(async (insight: any) => {
        const creative = campaignCreatives[insight.campaign_id];
        
        // Fetch latest AI recommendation from DB if exists | جلب آخر توصية من الذكاء الاصطناعي إذا وجدت
        let aiRecommendation: string | undefined;
        try {
          const latestAnalysis = await this.aiAnalysisRepository.findOne({
            where: { campaignId: insight.campaign_id },
            order: { createdAt: 'DESC' }
          });
          if (latestAnalysis) {
            aiRecommendation = latestAnalysis.analysisText;
          }
        } catch (e) {
          this.logger.warn(`Failed to fetch cached analysis for ${insight.campaign_id}`);
        }

        return {
          ...insight,
          effective_status: campaignStatuses[insight.campaign_id] || 'UNKNOWN',
          image_url: creative?.imageUrl || '',
          ad_copy: creative?.adCopy || '',
          platform: creative?.platform || 'facebook', // Default to facebook if unknown
          aiRecommendation,
        };
      }));

      const normalized = MetaNormalizer.normalizeList(enrichedData);

      // 4. Persistence: Save/Update in DB | الحفظ/التحديث في قاعدة البيانات
      if (dto.userId && normalized.length > 0) {
        const connectedAccount = await this.accountRepository.findOne({
          where: { externalAdAccountId: finalAdAccountId, user: { id: dto.userId } }
        });
        if (connectedAccount) {
          await this.upsertCampaigns(normalized, dto.userId, connectedAccount.id);
        }
      }

      return normalized;
    } catch (error) {
      const errorMsg = error.response?.data?.error?.message || error.message;
      this.logger.error(`Meta API Error: ${errorMsg}`);

      throw new HttpException(
        {
          message: 'Failed to fetch insights from Meta API',
          detail: errorMsg,
        },
        HttpStatus.BAD_GATEWAY, // 502 Bad Gateway is appropriate for external API failures
      );
    }
  }
  // دالة لتنظيف رابط الصورة وجلب النسخة الأصلية عالية الجودة
  // قم بإضافتها كـ private method داخل كلاس MetaAdsService
  private cleanImageUrl(url: string): string {
    if (!url) return '';

    try {
      const urlObj = new URL(url);
      const params = urlObj.searchParams;

      // إذا وجدنا متغير stp، نقوم بحذفه ليرجع السيرفر الصورة الأصلية دون قص أو تصغير
      if (params.has('stp')) {
        params.delete('stp');
        this.logger.debug('Image URL cleaned for high-res fetch.');
      }

      return urlObj.toString();
    } catch (e) {
      // في حال فشل تحليل الرابط لسبب ما، نرجع الرابط الأصلي
      return url;
    }
  }
  /**
   * Connect an Ad Account
   * ربط حساب إعلاني
   */
  async connectAccount(dto: ConnectAccountDto): Promise<ConnectedAccount> {
    const user = await this.userRepository.findOne({ where: { id: dto.userId } });
    if (!user) {
      throw new Error('User not found');
    }

    let finalToken = dto.accessToken;

    // Verify token with Meta if it's a Meta platform and exchange for long-lived
    if (dto.platform === 'meta') {
      await this.verifyTokenWithMeta(dto.accessToken);

      try {
        this.logger.log('Exchanging short-lived token for long-lived token...');
        const longLivedToken = await this.getLongLivedToken(dto.accessToken);
        if (longLivedToken) {
          finalToken = longLivedToken;
          this.logger.log('Successfully obtained long-lived token.');
        }
      } catch (error) {
        this.logger.warn('Failed to obtain long-lived token, falling back to short-lived.', error.message);
      }
    }

    // Check if account already exists for this user and platform
    let account = await this.accountRepository.findOne({
      where: {
        externalAdAccountId: dto.adAccountId,
        platform: dto.platform,
        user: { id: dto.userId }
      }
    });

    if (account) {
      this.logger.log(`Updating existing ${dto.platform} account: ${dto.adAccountId}`);
      account.accessToken = finalToken;
      account.name = dto.name;
    } else {
      this.logger.log(`Creating new ${dto.platform} account: ${dto.adAccountId}`);
      account = this.accountRepository.create({
        name: dto.name,
        platform: dto.platform,
        externalAdAccountId: dto.adAccountId,
        accessToken: finalToken,
        user,
      });
    }

    return this.accountRepository.save(account);
  }

  /**
   * Verify Access Token with Meta Graph API
   * التحقق من توكن الوصول عبر واجهة ميتا غراف
   */
  private async verifyTokenWithMeta(accessToken: string): Promise<void> {
    const url = `https://graph.facebook.com/${this.apiVersion}/me`;

    try {
      await firstValueFrom(
        this.httpService.get(url, { params: { access_token: accessToken } })
      );
      this.logger.log('Meta token verified successfully');
    } catch (error) {
      const errorData = error.response?.data?.error;
      const message = errorData?.message || 'Invalid Meta Access Token';

      this.logger.error(`Token verification failed: ${message}`);
      throw new HttpException(
        {
          message: 'فشل التحقق من التوكن مع ميتـا | Meta Token Verification Failed',
          detail: message,
          code: errorData?.code || 'INVALID_TOKEN',
        },
        HttpStatus.UNAUTHORIZED
      );
    }
  }

  /**
   * Exchange OAuth Code for User Access Token
   * استبدال رمز OAuth بتوكن وصول المستخدم
   */
  async exchangeCodeForToken(code: string): Promise<string> {
    const appId = this.configService.get<string>('meta.appId');
    const appSecret = this.configService.get<string>('meta.appSecret');
    const redirectUri = this.configService.get<string>('meta.redirectUri');
    const url = `https://graph.facebook.com/${this.apiVersion}/oauth/access_token`;

    try {
      const response = await firstValueFrom(
        this.httpService.get(url, {
          params: {
            client_id: appId,
            client_secret: appSecret,
            redirect_uri: redirectUri,
            code: code,
          },
        })
      );
      return response.data.access_token;
    } catch (error) {
      throw new HttpException('Failed to exchange code for token', HttpStatus.BAD_GATEWAY);
    }
  }

  /**
   * Exchange Short-lived Token for Long-lived Token (60 days)
   * استبدال التوكن قصير الأمد بآخر طويل الأمد (60 يوماً)
   */
  async getLongLivedToken(shortLivedToken: string): Promise<string> {
    const appId = this.configService.get<string>('meta.appId');
    const appSecret = this.configService.get<string>('meta.appSecret');
    const url = `https://graph.facebook.com/${this.apiVersion}/oauth/access_token`;

    try {
      const response = await firstValueFrom(
        this.httpService.get(url, {
          params: {
            grant_type: 'fb_exchange_token',
            client_id: appId,
            client_secret: appSecret,
            fb_exchange_token: shortLivedToken,
          },
        })
      );
      return response.data.access_token;
    } catch (error) {
      throw new HttpException('Failed to get long-lived token', HttpStatus.BAD_GATEWAY);
    }
  }

  /**
   * Get User Ad Accounts
   * جلب الحسابات الإعلانية للمستخدم
   */
  async getAdAccounts(accessToken: string): Promise<any[]> {
    const url = `https://graph.facebook.com/${this.apiVersion}/me/adaccounts`;

    try {
      const response = await firstValueFrom(
        this.httpService.get(url, {
          params: {
            access_token: accessToken,
            fields: 'id,name,account_id',
          },
        })
      );
      return response.data.data;
    } catch (error) {
      throw new HttpException('Failed to fetch ad accounts', HttpStatus.BAD_GATEWAY);
    }
  }

  /**
   * Update the status of an Ad object (Campaign, AdSet, or Ad)
   * تحديث حالة الكائن الإعلاني (حملة، مجموعة، أو إعلان)
   */
  async updateAdStatus(id: string, accessToken: string, status: 'PAUSED' | 'ACTIVE'): Promise<boolean> {
    const url = `https://graph.facebook.com/${this.apiVersion}/${id}`;

    try {
      this.logger.log(`Updating Meta object ${id} status to ${status}`);
      await firstValueFrom(
        this.httpService.post(url, {
          status,
          access_token: accessToken,
        })
      );
      return true;
    } catch (error) {
      const errorMsg = error.response?.data?.error?.message || error.message;
      this.logger.error(`Failed to update Meta status for ${id}: ${errorMsg}`);
      throw new HttpException(
        {
          message: 'Failed to update Meta Ad status',
          detail: errorMsg,
        },
        HttpStatus.BAD_GATEWAY
      );
    }
  }

  /**
   * Upsert campaigns into the database
   * حفظ أو تحديث الحملات في قاعدة البيانات
   */
  private async upsertCampaigns(items: NormalizedCampaignInsight[], userId: string, accountId: string): Promise<void> {
    const today = new Date();
    
    for (const item of items) {
      try {
        let campaign = await this.campaignRepository.findOne({
          where: { campaignId: item.campaignId, userId }
        });

        if (campaign) {
          // Update existing | تحديث الموجود
          Object.assign(campaign, {
            campaignName: item.campaignName,
            status: item.status,
            impressions: item.impressions,
            clicks: item.clicks,
            spend: item.spend,
            ctr: item.ctr,
            cpc: item.cpc,
            cpm: item.cpm,
            imageUrl: item.imageUrl,
            adCopy: item.adCopy,
            platform: item.platform,
            lastSyncedAt: today,
            accountId,
          });
        } else {
          // Create new | إنشاء جديد
          campaign = this.campaignRepository.create({
            campaignId: item.campaignId,
            campaignName: item.campaignName,
            status: item.status,
            impressions: item.impressions,
            clicks: item.clicks,
            spend: item.spend,
            ctr: item.ctr,
            cpc: item.cpc,
            cpm: item.cpm,
            imageUrl: item.imageUrl,
            adCopy: item.adCopy,
            platform: item.platform,
            lastSyncedAt: today,
            userId,
            accountId,
          });
        }

        await this.campaignRepository.save(campaign);
      } catch (error) {
        this.logger.warn(`Failed to upsert campaign ${item.campaignId}: ${error.message}`);
      }
    }
  }

  /**
   * Get all connected accounts for a user
   * جلب جميع الحسابات المرتبطة للمستخدم
   */
  async getConnectedAccounts(userId: string): Promise<ConnectedAccount[]> {
    return this.accountRepository.find({
      where: { user: { id: userId } },
      order: { createdAt: 'DESC' },
    });
  }
}
