import {
  Injectable,
  NotFoundException,
  ForbiddenException,
  BadRequestException,
  Inject,
  forwardRef,
  Optional,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, Between, MoreThanOrEqual, LessThanOrEqual } from 'typeorm';
import {
  Activity,
  ActivityType,
} from '../../database/entities/activity.entity';
import { Child } from '../../database/entities/child.entity';
import { FamilyMember } from '../../database/entities/family-member.entity';
import { FamiliesGateway } from '../families/families.gateway';
import { CreateActivityDto } from './dto/create-activity.dto';
import { UpdateActivityDto } from './dto/update-activity.dto';
import { CreateBulkActivitiesDto } from './dto/create-bulk-activities.dto';

@Injectable()
export class TrackingService {
  constructor(
    @InjectRepository(Activity)
    private activityRepository: Repository<Activity>,
    @InjectRepository(Child)
    private childRepository: Repository<Child>,
    @InjectRepository(FamilyMember)
    private familyMemberRepository: Repository<FamilyMember>,
    @Optional()
    @Inject(forwardRef(() => FamiliesGateway))
    private familiesGateway?: FamiliesGateway,
  ) {}

  async create(
    userId: string,
    childId: string,
    createActivityDto: CreateActivityDto,
  ): Promise<Activity> {
    // Verify child exists
    const child = await this.childRepository.findOne({
      where: { id: childId },
    });

    if (!child) {
      throw new NotFoundException('Child not found');
    }

    // Verify user has permission to log activities
    const membership = await this.familyMemberRepository.findOne({
      where: { userId, familyId: child.familyId },
    });

    if (!membership) {
      throw new ForbiddenException('You do not have access to this child');
    }

    if (!membership.permissions['canLogActivities']) {
      throw new ForbiddenException(
        'You do not have permission to log activities for this child',
      );
    }

    // Create activity
    const activity = this.activityRepository.create({
      ...createActivityDto,
      childId,
      loggedBy: userId,
      startedAt: new Date(createActivityDto.startedAt),
      endedAt: createActivityDto.endedAt
        ? new Date(createActivityDto.endedAt)
        : null,
    });

    const savedActivity = await this.activityRepository.save(activity);

    // Emit WebSocket event to family members
    if (this.familiesGateway) {
      this.familiesGateway.notifyFamilyActivityCreated(
        child.familyId,
        savedActivity,
      );
    }

    return savedActivity;
  }

  async findAll(
    userId: string,
    childId: string,
    type?: string,
    startDate?: string,
    endDate?: string,
  ): Promise<Activity[]> {
    // Verify child exists and user has access
    const child = await this.childRepository.findOne({
      where: { id: childId },
    });

    if (!child) {
      throw new NotFoundException('Child not found');
    }

    const membership = await this.familyMemberRepository.findOne({
      where: { userId, familyId: child.familyId },
    });

    if (!membership) {
      throw new ForbiddenException('You do not have access to this child');
    }

    // Build query
    const where: any = { childId };

    if (type) {
      where.type = type;
    }

    if (startDate && endDate) {
      where.startedAt = Between(new Date(startDate), new Date(endDate));
    } else if (startDate) {
      where.startedAt = MoreThanOrEqual(new Date(startDate));
    } else if (endDate) {
      where.startedAt = LessThanOrEqual(new Date(endDate));
    }

    return await this.activityRepository.find({
      where,
      order: {
        startedAt: 'DESC',
      },
      relations: ['child', 'logger'],
    });
  }

  async findOne(userId: string, id: string): Promise<Activity> {
    const activity = await this.activityRepository.findOne({
      where: { id },
      relations: ['child', 'logger'],
    });

    if (!activity) {
      throw new NotFoundException('Activity not found');
    }

    // Verify user has access to the child
    const membership = await this.familyMemberRepository.findOne({
      where: { userId, familyId: activity.child.familyId },
    });

    if (!membership) {
      throw new ForbiddenException('You do not have access to this activity');
    }

    return activity;
  }

  async update(
    userId: string,
    id: string,
    updateActivityDto: UpdateActivityDto,
  ): Promise<Activity> {
    const activity = await this.activityRepository.findOne({
      where: { id },
      relations: ['child'],
    });

    if (!activity) {
      throw new NotFoundException('Activity not found');
    }

    // Verify user has permission
    const membership = await this.familyMemberRepository.findOne({
      where: { userId, familyId: activity.child.familyId },
    });

    if (!membership) {
      throw new ForbiddenException('You do not have access to this activity');
    }

    if (!membership.permissions['canLogActivities']) {
      throw new ForbiddenException(
        'You do not have permission to edit activities',
      );
    }

    // Update activity
    if (updateActivityDto.type !== undefined) {
      activity.type = updateActivityDto.type;
    }
    if (updateActivityDto.startedAt !== undefined) {
      activity.startedAt = new Date(updateActivityDto.startedAt);
    }
    if (updateActivityDto.endedAt !== undefined) {
      activity.endedAt = updateActivityDto.endedAt
        ? new Date(updateActivityDto.endedAt)
        : null;
    }
    if (updateActivityDto.notes !== undefined) {
      activity.notes = updateActivityDto.notes;
    }
    if (updateActivityDto.metadata !== undefined) {
      activity.metadata = updateActivityDto.metadata;
    }

    return await this.activityRepository.save(activity);
  }

  async remove(userId: string, id: string): Promise<void> {
    const activity = await this.activityRepository.findOne({
      where: { id },
      relations: ['child'],
    });

    if (!activity) {
      throw new NotFoundException('Activity not found');
    }

    // Verify user has permission
    const membership = await this.familyMemberRepository.findOne({
      where: { userId, familyId: activity.child.familyId },
    });

    if (!membership) {
      throw new ForbiddenException('You do not have access to this activity');
    }

    if (!membership.permissions['canLogActivities']) {
      throw new ForbiddenException(
        'You do not have permission to delete activities',
      );
    }

    await this.activityRepository.remove(activity);
  }

  /**
   * Get daily summary for a child
   */
  async getDailySummary(
    userId: string,
    childId: string,
    date: string,
  ): Promise<Record<string, any>> {
    // Verify child exists and user has access
    const child = await this.childRepository.findOne({
      where: { id: childId },
    });

    if (!child) {
      throw new NotFoundException('Child not found');
    }

    const membership = await this.familyMemberRepository.findOne({
      where: { userId, familyId: child.familyId },
    });

    if (!membership) {
      throw new ForbiddenException('You do not have access to this child');
    }

    // Get activities for the day
    const startOfDay = new Date(date);
    startOfDay.setHours(0, 0, 0, 0);

    const endOfDay = new Date(date);
    endOfDay.setHours(23, 59, 59, 999);

    const activities = await this.activityRepository.find({
      where: {
        childId,
        startedAt: Between(startOfDay, endOfDay),
      },
      order: {
        startedAt: 'ASC',
      },
    });

    // Calculate summary statistics
    let feedingCount = 0;
    let sleepTotalMinutes = 0;
    let diaperCount = 0;
    let medicationCount = 0;

    const activityList = [];
    const byType: Record<string, any[]> = {};

    activities.forEach((activity) => {
      const activityData = {
        id: activity.id,
        type: activity.type,
        startedAt: activity.startedAt,
        endedAt: activity.endedAt,
        notes: activity.notes,
        metadata: activity.metadata,
      };

      activityList.push(activityData);

      if (!byType[activity.type]) {
        byType[activity.type] = [];
      }
      byType[activity.type].push(activity);

      // Count by type
      if (activity.type === ActivityType.FEEDING) {
        feedingCount++;
      } else if (activity.type === ActivityType.SLEEP) {
        // Calculate sleep duration in minutes
        if (activity.startedAt && activity.endedAt) {
          const durationMs =
            new Date(activity.endedAt).getTime() -
            new Date(activity.startedAt).getTime();
          sleepTotalMinutes += Math.floor(durationMs / 60000);
        }
      } else if (activity.type === ActivityType.DIAPER) {
        diaperCount++;
      } else if (activity.type === ActivityType.MEDICATION) {
        medicationCount++;
      }
    });

    return {
      date,
      childId,
      feedingCount,
      sleepTotalMinutes,
      diaperCount,
      medicationCount,
      activities: activityList,
      byType,
    };
  }

  /**
   * Create activities for multiple children simultaneously (bulk logging)
   */
  async createBulkActivities(
    userId: string,
    createBulkDto: CreateBulkActivitiesDto,
  ): Promise<Activity[]> {
    const { childIds, ...activityData } = createBulkDto;

    // Verify all children exist and user has access
    const children = await this.childRepository
      .createQueryBuilder('child')
      .where('child.id IN (:...childIds)', { childIds })
      .getMany();

    if (children.length !== childIds.length) {
      throw new NotFoundException(
        'One or more children not found',
      );
    }

    // Verify user has permission for all children
    const familyIds = [...new Set(children.map((c) => c.familyId))];
    for (const familyId of familyIds) {
      const membership = await this.familyMemberRepository.findOne({
        where: { userId, familyId },
      });

      if (!membership) {
        throw new ForbiddenException(
          `You do not have access to one or more children`,
        );
      }

      if (!membership.permissions['canLogActivities']) {
        throw new ForbiddenException(
          `You do not have permission to log activities for this family`,
        );
      }
    }

    // Generate bulk operation ID to link activities
    const bulkOperationId = this.generateUUID();

    // Create activities for all children
    const activities = childIds.map((childId) =>
      this.activityRepository.create({
        ...activityData,
        childId,
        loggedBy: userId,
        startedAt: new Date(activityData.startedAt),
        endedAt: activityData.endedAt
          ? new Date(activityData.endedAt)
          : null,
        bulkOperationId,
        siblingsCount: childIds.length,
      }),
    );

    const savedActivities = await this.activityRepository.save(activities);

    // Emit WebSocket events for each family
    if (this.familiesGateway) {
      for (const familyId of familyIds) {
        const familyActivities = savedActivities.filter((activity) =>
          children.some(
            (c) => c.id === activity.childId && c.familyId === familyId,
          ),
        );

        this.familiesGateway.notifyFamilyBulkActivitiesCreated(
          familyId,
          familyActivities,
          childIds.length,
        );
      }
    }

    return savedActivities;
  }

  /**
   * Get activities for multiple children (supports multi-child queries)
   */
  async getActivitiesForMultipleChildren(
    userId: string,
    childIds: string[],
    type?: ActivityType,
    startDate?: Date,
    endDate?: Date,
  ): Promise<Activity[]> {
    // Verify all children exist and user has access
    const children = await this.childRepository
      .createQueryBuilder('child')
      .where('child.id IN (:...childIds)', { childIds })
      .getMany();

    if (children.length === 0) {
      return [];
    }

    // Verify user has permission for all children
    const familyIds = [...new Set(children.map((c) => c.familyId))];
    for (const familyId of familyIds) {
      const membership = await this.familyMemberRepository.findOne({
        where: { userId, familyId },
      });

      if (!membership) {
        throw new ForbiddenException(
          'You do not have access to one or more children',
        );
      }
    }

    // Build query
    const queryBuilder = this.activityRepository
      .createQueryBuilder('activity')
      .where('activity.childId IN (:...childIds)', { childIds })
      .orderBy('activity.startedAt', 'DESC');

    if (type) {
      queryBuilder.andWhere('activity.type = :type', { type });
    }

    if (startDate) {
      queryBuilder.andWhere('activity.startedAt >= :startDate', { startDate });
    }

    if (endDate) {
      queryBuilder.andWhere('activity.startedAt <= :endDate', { endDate });
    }

    return queryBuilder.getMany();
  }

  private generateUUID(): string {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(
      /[xy]/g,
      function (c) {
        const r = (Math.random() * 16) | 0;
        const v = c === 'x' ? r : (r & 0x3) | 0x8;
        return v.toString(16);
      },
    );
  }
}
