/**
 * Social Calendar Service
 * Content calendar management, scheduling, and bulk operations
 *
 * @module lib/services/social-calendar.service
 */

import { logger } from '@/lib/logger';
import {
  socialContentQueueAdapter,
  socialAccountsAdapter,
} from '@/lib/adapters/social';
import { socialPosting } from './social-posting.service';
import type {
  SocialPlatform,
  SocialQueueStatus,
  CalendarEvent,
  ScheduleInput,
} from '@/lib/types/social-media.types';
function toCalendarEvent(item: Record<string, unknown>): CalendarEvent {
  const account = item.social_account as { accountName: string };
  const content = item.content as string;
  return {
    id: item.id as string,
    title: content.substring(0, 60) + (content.length > 60 ? '...' : ''),
    platform: item.platform as SocialPlatform,
    accountName: account.accountName,
    status: item.status as SocialQueueStatus,
    scheduledAt: item.scheduledAt as Date | null,
    content,
    hashtags: item.hashtags as string[],
    mediaUrls: item.mediaUrls as string[],
    calendarColor: item.calendarColor as string | null,
    campaignTag: item.campaignTag as string | null,
  };
}

// Cast helper: allows passing extra args (include, orderBy) beyond the adapter's narrow types
const queueAdapter = socialContentQueueAdapter as unknown as {
  findMany(args: Record<string, unknown>): Promise<unknown[]>;
  findFirst(args: Record<string, unknown>): Promise<unknown>;
  findUnique(args: Record<string, unknown>): Promise<unknown>;
  create(args: Record<string, unknown>): Promise<Record<string, unknown>>;
  update(args: Record<string, unknown>): Promise<Record<string, unknown>>;
  delete(args: Record<string, unknown>): Promise<unknown>;
};

export class SocialCalendarService {
  /**
   * Get calendar events for a given month
   */
  static async getCalendarEvents(
    userId: string,
    month: number,
    year: number,
    platform?: SocialPlatform,
    status?: SocialQueueStatus
  ): Promise<CalendarEvent[]> {
    const startDate = new Date(year, month - 1, 1);
    const endDate = new Date(year, month, 0, 23, 59, 59);

    const where: Record<string, unknown> = {
      social_account: { userId },
      OR: [
        { scheduledAt: { gte: startDate, lte: endDate } },
        { publishedAt: { gte: startDate, lte: endDate } },
        { status: 'DRAFT', createdAt: { gte: startDate, lte: endDate } },
      ],
    };

    if (platform) where.platform = platform;
    if (status) where.status = status;

    const items = await queueAdapter.findMany({
      where,
      include: { social_account: { select: { accountName: true } } },
      orderBy: [{ scheduledAt: 'asc' }, { createdAt: 'asc' }],
    });

    return (items as Array<Record<string, unknown>>).map(toCalendarEvent);
  }

  /**
   * Schedule a post
   */
  static async schedulePost(
    userId: string,
    input: ScheduleInput
  ): Promise<CalendarEvent> {
    // Verify account ownership
    const account = await socialAccountsAdapter.findFirst({
      where: { id: input.socialAccountId, userId },
    });

    if (!account) {
      throw new Error('Social account not found or unauthorized');
    }

    // Validate schedule time is in the future
    if (new Date(input.scheduledAt) <= new Date()) {
      throw new Error('Scheduled time must be in the future');
    }

    const item = await queueAdapter.create({
      data: {
        socialAccountId: input.socialAccountId,
        platform: input.platform,
        content: input.content,
        hashtags: input.hashtags || [],
        mediaUrls: input.mediaUrls || [],
        status: 'SCHEDULED',
        scheduledAt: new Date(input.scheduledAt),
        calendarColor: input.calendarColor || null,
        campaignTag: input.campaignTag || null,
      },
      include: { social_account: { select: { accountName: true } } },
    });

    logger.info('Post scheduled', {
      userId,
      itemId: item.id,
      scheduledAt: input.scheduledAt,
    });

    return toCalendarEvent(item);
  }

  /**
   * Bulk schedule multiple posts
   */
  static async bulkSchedule(
    userId: string,
    items: ScheduleInput[]
  ): Promise<{ scheduled: number; failed: number; results: CalendarEvent[] }> {
    let scheduled = 0;
    let failed = 0;
    const results: CalendarEvent[] = [];

    for (const input of items) {
      try {
        const event = await this.schedulePost(userId, input);
        results.push(event);
        scheduled++;
      } catch (error) {
        logger.warn('Bulk schedule item failed', { error, input });
        failed++;
      }
    }

    return { scheduled, failed, results };
  }

  /**
   * Reschedule an existing post
   */
  static async reschedule(
    userId: string,
    itemId: string,
    newDate: Date
  ): Promise<CalendarEvent> {
    const item = await queueAdapter.findFirst({
      where: {
        id: itemId,
        social_account: { userId },
        status: { in: ['DRAFT', 'SCHEDULED'] },
      },
    });

    if (!item) {
      throw new Error('Queue item not found, not owned by user, or already published');
    }

    if (new Date(newDate) <= new Date()) {
      throw new Error('New scheduled time must be in the future');
    }

    const updated = await queueAdapter.update({
      where: { id: itemId },
      data: {
        scheduledAt: new Date(newDate),
        status: 'SCHEDULED',
      },
      include: { social_account: { select: { accountName: true } } },
    });

    logger.info('Post rescheduled', { userId, itemId, newDate });

    return toCalendarEvent(updated);
  }

  /**
   * Delete a scheduled/draft post
   */
  static async deleteScheduledPost(userId: string, itemId: string): Promise<void> {
    const item = await queueAdapter.findFirst({
      where: {
        id: itemId,
        social_account: { userId },
        status: { in: ['DRAFT', 'SCHEDULED'] },
      },
    });

    if (!item) {
      throw new Error('Queue item not found, not owned by user, or already published');
    }

    await queueAdapter.delete({ where: { id: itemId } });

    logger.info('Scheduled post deleted', { userId, itemId });
  }

  /**
   * Publish a draft/scheduled post immediately
   */
  static async publishNow(userId: string, itemId: string): Promise<CalendarEvent> {
    const item = await queueAdapter.findFirst({
      where: {
        id: itemId,
        social_account: { userId },
        status: { in: ['DRAFT', 'SCHEDULED'] },
      },
    });

    if (!item) {
      throw new Error('Queue item not found, not owned by user, or already published');
    }

    const result = await socialPosting.publishQueueItem(itemId);

    if (!result.success) {
      throw new Error(result.error || 'Publishing failed');
    }

    const updated = await queueAdapter.findUnique({
      where: { id: itemId },
      include: { social_account: { select: { accountName: true } } },
    });

    if (!updated) throw new Error('Post not found after publishing');

    return toCalendarEvent(updated as Record<string, unknown>);
  }

  /**
   * Get upcoming scheduled posts
   */
  static async getUpcoming(
    userId: string,
    limit: number = 10
  ): Promise<CalendarEvent[]> {
    const items = await queueAdapter.findMany({
      where: {
        social_account: { userId },
        status: 'SCHEDULED',
        scheduledAt: { gt: new Date() },
      },
      include: { social_account: { select: { accountName: true } } },
      orderBy: { scheduledAt: 'asc' },
      take: limit,
    });

    return (items as Array<Record<string, unknown>>).map(toCalendarEvent);
  }

  /**
   * Get all draft posts
   */
  static async getDrafts(userId: string): Promise<CalendarEvent[]> {
    const items = await queueAdapter.findMany({
      where: {
        social_account: { userId },
        status: 'DRAFT',
      },
      include: { social_account: { select: { accountName: true } } },
      orderBy: { createdAt: 'desc' },
    });

    return (items as Array<Record<string, unknown>>).map(toCalendarEvent);
  }
}

export const socialCalendar = new SocialCalendarService();
