import {
  Controller,
  Get,
  Post,
  Put,
  Delete,
  Param,
  Body,
  Query,
  Headers,
  UnauthorizedException,
  NotFoundException,
  HttpCode,
  HttpStatus,
  ParseIntPipe,
  DefaultValuePipe,
} from '@nestjs/common';
import { CampaignRepository, CreateCampaignDto } from '../../../infrastructure/repositories/campaign.repository';
import * as jwt from 'jsonwebtoken';

/**
 * Platform-admin Campaign management endpoints.
 * A campaign wraps a notification + schedule logic (ONCE or RECURRING cron).
 */
@Controller('api/v1/notifications/campaigns')
export class CampaignAdminController {
  constructor(private readonly repo: CampaignRepository) {}

  /** POST /api/v1/notifications/campaigns — create */
  @Post()
  async create(
    @Headers('authorization') auth: string,
    @Body() body: any,
  ) {
    const admin = this.requireAdmin(auth);
    return this.repo.create({
      name: body.name,
      description: body.description,
      notificationId: body.notificationId,
      scheduleType: body.scheduleType ?? 'ONCE',
      scheduledAt: body.scheduledAt ? new Date(body.scheduledAt) : null,
      cronExpr: body.cronExpr ?? null,
      timezone: body.timezone ?? 'UTC',
      createdBy: admin.userId,
    } as CreateCampaignDto);
  }

  /** GET /api/v1/notifications/campaigns — list */
  @Get()
  async list(
    @Headers('authorization') auth: string,
    @Query('status') status: string | undefined,
    @Query('limit', new DefaultValuePipe(20), ParseIntPipe) limit: number,
    @Query('offset', new DefaultValuePipe(0), ParseIntPipe) offset: number,
  ) {
    this.requireAdmin(auth);
    return this.repo.findAll({ status, limit: Math.min(limit, 100), offset });
  }

  /** GET /api/v1/notifications/campaigns/:id — detail */
  @Get(':id')
  async getOne(
    @Headers('authorization') auth: string,
    @Param('id') id: string,
  ) {
    this.requireAdmin(auth);
    const item = await this.repo.findById(id);
    if (!item) throw new NotFoundException('Campaign not found');
    return item;
  }

  /** GET /api/v1/notifications/campaigns/:id/stats — analytics */
  @Get(':id/stats')
  async getStats(
    @Headers('authorization') auth: string,
    @Param('id') id: string,
  ) {
    this.requireAdmin(auth);
    const item = await this.repo.findById(id);
    if (!item) throw new NotFoundException('Campaign not found');
    return this.repo.getStats(id);
  }

  /** PUT /api/v1/notifications/campaigns/:id — update */
  @Put(':id')
  async update(
    @Headers('authorization') auth: string,
    @Param('id') id: string,
    @Body() body: any,
  ) {
    this.requireAdmin(auth);
    const dto: any = {};
    if (body.name !== undefined) dto.name = body.name;
    if (body.description !== undefined) dto.description = body.description;
    if (body.notificationId !== undefined) dto.notificationId = body.notificationId;
    if (body.scheduleType !== undefined) dto.scheduleType = body.scheduleType;
    if (body.scheduledAt !== undefined) dto.scheduledAt = body.scheduledAt ? new Date(body.scheduledAt) : null;
    if (body.cronExpr !== undefined) dto.cronExpr = body.cronExpr;
    if (body.timezone !== undefined) dto.timezone = body.timezone;

    const updated = await this.repo.update(id, dto);
    if (!updated) throw new NotFoundException('Campaign not found');
    return updated;
  }

  /** POST /api/v1/notifications/campaigns/:id/schedule — schedule / re-schedule */
  @Post(':id/schedule')
  async schedule(
    @Headers('authorization') auth: string,
    @Param('id') id: string,
    @Body() body: { scheduledAt?: string; cronExpr?: string; timezone?: string },
  ) {
    this.requireAdmin(auth);
    const campaign = await this.repo.findById(id);
    if (!campaign) throw new NotFoundException('Campaign not found');

    const update: any = { status: 'SCHEDULED' };
    if (body.scheduledAt) {
      update.scheduledAt = new Date(body.scheduledAt);
      update.nextRunAt = new Date(body.scheduledAt);
    }
    if (body.cronExpr !== undefined) update.cronExpr = body.cronExpr;
    if (body.timezone !== undefined) update.timezone = body.timezone;

    return this.repo.update(id, update);
  }

  /** POST /api/v1/notifications/campaigns/:id/cancel — cancel */
  @Post(':id/cancel')
  async cancel(
    @Headers('authorization') auth: string,
    @Param('id') id: string,
  ) {
    this.requireAdmin(auth);
    const updated = await this.repo.update(id, { status: 'CANCELLED' });
    if (!updated) throw new NotFoundException('Campaign not found');
    return updated;
  }

  /** DELETE /api/v1/notifications/campaigns/:id — delete DRAFT campaigns only */
  @Delete(':id')
  @HttpCode(HttpStatus.NO_CONTENT)
  async remove(
    @Headers('authorization') auth: string,
    @Param('id') id: string,
  ) {
    this.requireAdmin(auth);
    const campaign = await this.repo.findById(id);
    if (!campaign) throw new NotFoundException('Campaign not found');
    await this.repo.delete(id);
  }

  private requireAdmin(auth: string): { userId: string } {
    if (!auth?.startsWith('Bearer ')) throw new UnauthorizedException('Missing authorization header');
    const token = auth.slice(7);
    try {
      const payload = jwt.verify(token, process.env.JWT_SECRET || 'dev-secret') as any;
      const roles: string[] = Array.isArray(payload.roles) ? payload.roles : [];
      if (!roles.includes('platform_admin') && !roles.includes('platform_super_admin')) {
        throw new UnauthorizedException('Admin role required');
      }
      return { userId: payload.sub };
    } catch (err) {
      if (err instanceof UnauthorizedException) throw err;
      throw new UnauthorizedException('Invalid JWT');
    }
  }
}
