import {
  Controller,
  Post,
  Body,
  Param,
  Get,
  Put,
  Delete,
  HttpException,
  HttpStatus,
  Query,
  Res,
} from '@nestjs/common';
import { Response } from 'express';
import { FranchisesService } from './franchises.service';
import { AnalyticsService } from '../analytics/analytics.service';
import { ReportsHistoryService } from '../reports_history/reports_history.service';
import { Public } from '../auth/public.decorator';
import {
  ApiBearerAuth,
  ApiTags,
  ApiBody,
  ApiParam,
  ApiQuery,
  ApiOperation,
} from '@nestjs/swagger';

@ApiTags('franchises')
@ApiBearerAuth('access-token')
@Controller('franchises')
export class FranchisesController {
  constructor(
    private readonly franchisesService: FranchisesService,
    private readonly analyticsService: AnalyticsService,
    private readonly reportsHistoryService: ReportsHistoryService,
  ) {}

  @Public()
  @Post(':franchiseId/add_company')
  @ApiOperation({ summary: 'Add Company For Franchise' })
  @ApiParam({ name: 'franchiseId', type: Number })
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        companyName: { type: 'string' },
        email: { type: 'string', format: 'email' },
        industry_id: { type: 'number' },
        company_size_id: { type: 'number' },
        website: { type: 'string' },
        phone: { type: 'string' },
      },
      required: ['companyName', 'email'],
    },
  })
  async addCompanyForFranchise(
    @Param('franchiseId') franchiseId: number,
    @Body() companyData: any,
  ) {
    return await this.franchisesService.addCompanyForFranchise(
      franchiseId,
      companyData,
    );
  }

  @Public()
  @Get()
  async findAll() {
    return await this.franchisesService.findAll();
  }
  //franchise list show by admin
  @Public()
  @Get('list_franchises')
  @ApiOperation({ summary: 'List Franchises (Admin)' })
  @ApiQuery({ name: 'page', required: false, type: Number })
  @ApiQuery({ name: 'limit', required: false, type: Number })
  @ApiQuery({ name: 'search', required: false, type: String })
  @ApiQuery({ name: 'status', required: false, type: String })
  async getFranchises(@Query() query) {
    try {
      const page = query.page ? Number(query.page) : 1;
      const limit = query.limit ? Number(query.limit) : 10;
      const searchTerm = query.search || undefined;
      const statusFilter = query.status || undefined;
      const result = await this.franchisesService.getFranchisesWithUser(
        page,
        limit,
        searchTerm,
        statusFilter,
      );
      //console.log('Franchises fetched successfully:', result);
      return result;
    } catch (error) {
      console.error('Error fetching franchises:', error);
      throw new HttpException(
        {
          message: error.message || 'Error fetching franchises',
          details: error.stack || 'No additional details available',
        },
        HttpStatus.INTERNAL_SERVER_ERROR,
      );
    }
  }
  @Public()
  @Get(':id')
  async findOne(@Param('id') id: number) {
    return await this.franchisesService.findOne(id);
  }

  @Public()
  @Post()
  @ApiOperation({ summary: 'Create Franchise' })
  @ApiBody({
    schema: {
      type: 'object',
      description: 'Franchise data',
      properties: {
        franchiseName: { type: 'string' },
        companyName: { type: 'string' },
        email: { type: 'string', format: 'email' },
        contactNo: { type: 'string' },
        address: { type: 'string' },
        countryId: { type: 'number' },
        stateId: { type: 'number' },
        cityId: { type: 'number' },
        postal_code: { type: 'string' },
        areaId: { type: 'number' },
        gstNo: { type: 'string' },
        serviceArea: { type: 'string' },
        status: { type: 'string', enum: ['active', 'inactive'] },
      },
      required: ['franchiseName', 'email'],
    },
  })
  async create(@Body() data: any) {
    return await this.franchisesService.create(data);
  }

  @Public()
  @Put(':id')
  @ApiOperation({ summary: 'Update Franchise' })
  @ApiParam({ name: 'id', type: Number })
  @ApiBody({
    schema: {
      type: 'object',
      description: 'Franchise data update',
      properties: {
        franchiseName: { type: 'string' },
        companyName: { type: 'string' },
        email: { type: 'string', format: 'email' },
        contactNo: { type: 'string' },
        address: { type: 'string' },
        countryId: { type: 'number' },
        stateId: { type: 'number' },
        cityId: { type: 'number' },
        postal_code: { type: 'string' },
        areaId: { type: 'number' },
        gstNo: { type: 'string' },
        serviceArea: { type: 'string' },
        status: { type: 'string', enum: ['active', 'inactive'] },
      },
    },
  })
  async update(@Param('id') id: number, @Body() data: any) {
    return await this.franchisesService.update(id, data);
  }

  @Public()
  @Delete(':id')
  async remove(@Param('id') id: number) {
    return await this.franchisesService.remove(id);
  }
  //admin franchise endpoints
  //  Create franchise + linked user

  @Public()
  @Post('add_franchise')
  @ApiOperation({ summary: 'Add Franchise With User' })
  @ApiBody({
    schema: {
      type: 'object',
      description: 'Create Franchise with Admin User',
      properties: {
        franchiseName: { type: 'string' },
        location: { type: 'string' },
        serviceArea: { type: 'string' },
        email: { type: 'string', format: 'email' },
        password: { type: 'string', minLength: 6 },
        username: { type: 'string' },
        phone: { type: 'string' },
      },
      required: ['franchiseName', 'email', 'password'],
    },
  })
  async createFranchise(@Body() body: any) {
    try {
      // You can add validation here manually if you don't want DTOs
      // if (!body.username || !body.email || !body.password || !body.franchiseName) {
      //   throw new HttpException(
      //     { message: 'Required fields are missing' },
      //     HttpStatus.BAD_REQUEST
      //   );
      // }
      const result = await this.franchisesService.createFranchiseWithUser(body);
      return result;
    } catch (error) {
      throw new HttpException(
        { message: error.message || 'Error creating franchise' },
        HttpStatus.INTERNAL_SERVER_ERROR,
      );
    }
  }

  //  Get list of franchises with user info

  @Public()
  @Put('update_franchise/:id')
  @ApiOperation({ summary: 'Update Franchise With User' })
  @ApiParam({ name: 'id', type: Number })
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        name: { type: 'string', description: 'Updates Franchise Name' },
        company: { type: 'string', description: 'Updates Company Name' },
        email: { type: 'string', format: 'email' },
        contactNumber: { type: 'string' },
        address: { type: 'string' },
        gstNo: { type: 'string' },
        serviceArea: {
          type: 'string',
          description: 'JSON string or array of service areas',
        },
        status: { type: 'string', enum: ['active', 'inactive'] },
        userStatus: { type: 'string', enum: ['active', 'inactive'] },
        password: {
          type: 'string',
          minLength: 6,
          description: 'Optional: Update password',
        },
      },
    },
  })
  async updateFranchise(@Param('id') id: number, @Body() body: any) {
    return await this.franchisesService.updateFranchiseWithUser(id, body);
  }

  // franchise.controller.ts
  @Public()
  @Put('update_status/:id')
  @ApiOperation({ summary: 'Update Franchise Status' })
  @ApiParam({ name: 'id', type: Number })
  @ApiBody({
    schema: {
      type: 'object',
      properties: { status: { type: 'string', enum: ['active', 'inactive'] } },
      required: ['status'],
    },
  })
  async updateStatus(
    @Param('id') id: number,
    @Body('status') status: 'active' | 'inactive',
  ) {
    return this.franchisesService.updateFranchiseStatus(id, status);
  }

  // Franchise Analytics Endpoints
  @Public()
  @Get(':franchiseId/analytics/overview')
  @ApiOperation({ summary: 'Get Franchise Analytics Overview' })
  @ApiParam({ name: 'franchiseId', type: Number })
  @ApiQuery({
    name: 'dateRange',
    required: false,
    type: Number,
    description: 'Days to look back',
  })
  async getFranchiseAnalyticsOverview(
    @Param('franchiseId') franchiseId: string,
    @Query('dateRange') dateRange: number = 30,
  ) {
    try {
      const franchiseIdNum = parseInt(franchiseId);
      // Use the provided dateRange parameter for filtered data

      const [
        jobAnalytics,
        applicationAnalytics,
        companyAnalytics,
        userAnalytics,
      ] = await Promise.all([
        this.analyticsService.getJobAnalytics(dateRange, franchiseIdNum),
        this.analyticsService.getApplicationAnalytics(
          dateRange,
          franchiseIdNum,
        ),
        this.analyticsService.getCompanyAnalytics(dateRange, franchiseIdNum),
        this.analyticsService.getUserAnalytics(dateRange, franchiseIdNum),
      ]);

      return {
        success: true,
        data: {
          totalJobs: jobAnalytics.totalJobs || 0,
          totalApplications: applicationAnalytics.totalApplications || 0,
          activeCompanies: companyAnalytics.activeCompanies || 0,
          activeUsers: userAnalytics.activeUsers || 0,
        },
      };
    } catch (error) {
      throw new HttpException(
        { message: error.message || 'Error fetching analytics overview' },
        HttpStatus.INTERNAL_SERVER_ERROR,
      );
    }
  }

  @Public()
  @Get(':franchiseId/analytics/jobs')
  @ApiOperation({ summary: 'Get Franchise Job Analytics' })
  @ApiParam({ name: 'franchiseId', type: Number })
  @ApiQuery({ name: 'dateRange', required: false, type: Number })
  async getFranchiseJobAnalytics(
    @Param('franchiseId') franchiseId: number,
    @Query('dateRange') dateRange: number = 30,
  ) {
    try {
      const analytics = await this.analyticsService.getJobAnalytics(
        dateRange,
        franchiseId,
      );
      return {
        success: true,
        data: analytics,
      };
    } catch (error) {
      throw new HttpException(
        { message: error.message || 'Error fetching job analytics' },
        HttpStatus.INTERNAL_SERVER_ERROR,
      );
    }
  }

  @Public()
  @Get(':franchiseId/analytics/applications')
  @ApiOperation({ summary: 'Get Franchise Application Analytics' })
  @ApiParam({ name: 'franchiseId', type: Number })
  @ApiQuery({ name: 'dateRange', required: false, type: Number })
  async getFranchiseApplicationAnalytics(
    @Param('franchiseId') franchiseId: number,
    @Query('dateRange') dateRange: number = 30,
  ) {
    try {
      const analytics = await this.analyticsService.getApplicationAnalytics(
        dateRange,
        franchiseId,
      );
      return {
        success: true,
        data: analytics,
      };
    } catch (error) {
      throw new HttpException(
        { message: error.message || 'Error fetching application analytics' },
        HttpStatus.INTERNAL_SERVER_ERROR,
      );
    }
  }

  @Public()
  @Get(':franchiseId/analytics/companies')
  async getFranchiseCompanyAnalytics(
    @Param('franchiseId') franchiseId: string,
    @Query('dateRange') dateRange: number = 30,
  ) {
    try {
      const franchiseIdNum = parseInt(franchiseId);
      const analytics = await this.analyticsService.getCompanyAnalytics(
        dateRange,
        franchiseIdNum,
      );
      return {
        success: true,
        data: analytics,
      };
    } catch (error) {
      throw new HttpException(
        { message: error.message || 'Error fetching company analytics' },
        HttpStatus.INTERNAL_SERVER_ERROR,
      );
    }
  }

  @Public()
  @Get(':franchiseId/analytics/users')
  async getFranchiseUserAnalytics(
    @Param('franchiseId') franchiseId: number,
    @Query('dateRange') dateRange: number = 30,
  ) {
    try {
      const analytics = await this.analyticsService.getUserAnalytics(
        dateRange,
        franchiseId,
      );
      return {
        success: true,
        data: analytics,
      };
    } catch (error) {
      throw new HttpException(
        { message: error.message || 'Error fetching user analytics' },
        HttpStatus.INTERNAL_SERVER_ERROR,
      );
    }
  }

  // Franchise Reports Endpoints
  @Public()
  @Get(':franchiseId/reports/history')
  async getFranchiseReportsHistory(@Param('franchiseId') franchiseId: number) {
    try {
      const result =
        await this.reportsHistoryService.getReportsByFranchiseId(franchiseId);
      return result;
    } catch (error) {
      throw new HttpException(
        { message: error.message || 'Error fetching reports history' },
        HttpStatus.INTERNAL_SERVER_ERROR,
      );
    }
  }

  @Public()
  @Post(':franchiseId/reports/generate')
  @ApiOperation({ summary: 'Generate Franchise Report' })
  @ApiParam({ name: 'franchiseId', type: Number })
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        type: { type: 'string', example: 'performance' },
        dateRange: { type: 'string', example: '30 days' },
        createdBy: { type: 'number' },
      },
      required: ['type', 'createdBy'],
    },
  })
  async generateFranchiseReport(
    @Param('franchiseId') franchiseId: number,
    @Body()
    reportData: {
      type: string;
      dateRange?: string;
      createdBy: number;
    },
  ) {
    try {
      // Parse date range if provided
      let dateRange = { start: new Date(), end: new Date() };
      if (reportData.dateRange) {
        const days = parseInt(reportData.dateRange.split(' ')[0]);
        const endDate = new Date();
        const startDate = new Date();
        startDate.setDate(endDate.getDate() - days);
        dateRange = { start: startDate, end: endDate };
      }

      const result = await this.reportsHistoryService.generateReport({
        type: reportData.type,
        dateRange,
        createdBy: reportData.createdBy,
        franchiseId,
      });

      return result;
    } catch (error) {
      throw new HttpException(
        { message: error.message || 'Error generating report' },
        HttpStatus.INTERNAL_SERVER_ERROR,
      );
    }
  }

  @Public()
  @Post(':franchiseId/reports/download/:reportId')
  async downloadFranchiseReport(
    @Param('franchiseId') franchiseId: number,
    @Param('reportId') reportId: number,
    @Res() res: Response,
  ) {
    try {
      console.log(
        `Downloading report ${reportId} for franchise ${franchiseId}`,
      );

      const result = await this.reportsHistoryService.downloadReport(
        reportId,
        franchiseId,
      );

      if (!result.success) {
        throw new HttpException(
          { message: result.error || 'Failed to download report' },
          HttpStatus.BAD_REQUEST,
        );
      }

      // Set proper headers for PDF download
      res.setHeader('Content-Type', 'application/pdf');
      res.setHeader(
        'Content-Disposition',
        `attachment; filename="${result.data.filename}"`,
      );
      res.setHeader('Content-Length', result.data.fileBuffer.length);

      // Send the PDF buffer
      res.send(result.data.fileBuffer);
    } catch (error) {
      console.error('Error in downloadFranchiseReport:', error);
      throw new HttpException(
        { message: error.message || 'Error downloading report' },
        HttpStatus.INTERNAL_SERVER_ERROR,
      );
    }
  }

  @Public()
  @Post(':franchiseId/reports/email')
  @ApiOperation({ summary: 'Email Franchise Report' })
  @ApiParam({ name: 'franchiseId', type: Number })
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        reportId: { type: 'number' },
        email: { type: 'string', format: 'email' },
      },
      required: ['reportId', 'email'],
    },
  })
  async emailFranchiseReport(
    @Param('franchiseId') franchiseId: number,
    @Body()
    emailData: {
      reportId: number;
      email: string;
    },
  ) {
    try {
      const result = await this.reportsHistoryService.emailReport({
        ...emailData,
        franchiseId, // Pass franchiseId for validation
      });
      return result;
    } catch (error) {
      throw new HttpException(
        { message: error.message || 'Error emailing report' },
        HttpStatus.INTERNAL_SERVER_ERROR,
      );
    }
  }
  @Public()
  @Get(':franchiseId/details')
  async getFranchiseDetails(@Param('franchiseId') franchiseId: number) {
    return await this.franchisesService.getFranchiseDetails(franchiseId);
  }
}
