import {
  Controller,
  Get,
  Post,
  Put,
  Patch,
  Delete,
  Body,
  Param,
  Query,
  Req,
  Res,
  HttpStatus,
  HttpCode,
  UseGuards,
  UsePipes,
  ValidationPipe,
  ParseIntPipe,
} from '@nestjs/common';
import { Response } from 'express';
import {
  ApiTags,
  ApiOperation,
  ApiResponse,
  ApiQuery,
  ApiBody,
  ApiBearerAuth,
  ApiSecurity,
  ApiParam,
} from '@nestjs/swagger';
import { ProgramTemplateService } from './program-template.service';
import { CreateProgramTemplateDto } from './dto/create-program-template.dto';
import { UpdateProgramTemplateDto } from './dto/update-program-template.dto';
import { UpdateTemplateFormDto } from './dto/update-template-form.dto';
import { FilterProgramTemplateDto } from './dto/filter-program-template.dto';
import { TemplateFormSectionService } from 'src/template-form-section/template-form-section.service';
import { CloneFromMasterDto } from 'src/template-form-section/dto/clone-from-master.dto';
import { TemplateFormBuilderDto, TemplateFormBuilderResponseDto } from 'src/template-form-section/dto/template-form-builder.dto';
import { ResponseService } from 'src/common/response-handling/response-handler';
import ErrorHandler from 'src/common/response-handling/error-handling';
import { Roles } from 'src/common/decorators/roles.decorator';
import { RolesGuard } from 'src/common/guards/roles.guard';
import { AppLoggerService } from 'src/common/services/logger.service';
import { handleControllerError } from 'src/common/utils/controller-response-handling';
import { CombinedAuthGuard } from 'src/auth/combined-auth.guard';
import { ROLE_VALUES } from 'src/common/constants/strings-constants';

@ApiTags('program-template')
@Controller('v1/program-templates')
@UseGuards(CombinedAuthGuard, RolesGuard)
@Roles(ROLE_VALUES.ADMIN, ROLE_VALUES.COORDINATOR)
@ApiBearerAuth('Authorization')
@ApiSecurity('userIdAuth')
@ApiSecurity('activeRoleAuth')
export class ProgramTemplateController {
  constructor(
    private readonly programTemplateService: ProgramTemplateService,
    private readonly templateFormSectionService: TemplateFormSectionService,
    private readonly responseService: ResponseService,
    private readonly errorHandler: ErrorHandler,
    private readonly logger: AppLoggerService,
  ) {}

  /**
   * Get all program templates with filters
   */
  @Get()
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Get all program templates with filters' })
  @ApiQuery({ name: 'limit', required: false, type: Number })
  @ApiQuery({ name: 'offset', required: false, type: Number })
  @ApiQuery({ name: 'searchText', required: false, type: String })
  @ApiQuery({ name: 'programTypeId', required: false, type: Number })
  @ApiQuery({ name: 'status', required: false, type: String })
  @ApiQuery({ name: 'isActive', required: false, type: Boolean })
  @ApiResponse({ status: HttpStatus.OK, description: 'Program templates retrieved successfully' })
  @ApiResponse({ status: HttpStatus.INTERNAL_SERVER_ERROR, description: 'Internal server error' })
  async findAll(@Query() filters: FilterProgramTemplateDto, @Res() res: Response) {
    this.logger.log('GET /v1/program-templates - Request received', { filters });
    try {
      const result = await this.programTemplateService.findAll(filters);
      this.logger.log('Program templates retrieved successfully');
      return this.responseService.success(res, 'Program templates retrieved successfully', result);
    } catch (error) {
      handleControllerError(res, error);
    }
  }

  /**
   * Get a single program template by ID
   */
  @Get(':id')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Get a program template by ID' })
  @ApiParam({ name: 'id', type: Number })
  @ApiResponse({ status: HttpStatus.OK, description: 'Program template retrieved successfully' })
  @ApiResponse({ status: HttpStatus.NOT_FOUND, description: 'Program template not found' })
  @ApiResponse({ status: HttpStatus.INTERNAL_SERVER_ERROR, description: 'Internal server error' })
  async findById(@Param('id', ParseIntPipe) id: number, @Res() res: Response) {
    this.logger.log('GET /v1/program-templates/:id - Request received', { id });
    try {
      const result = await this.programTemplateService.findById(id);
      this.logger.log('Program template retrieved successfully', { templateId: id });
      return this.responseService.success(res, 'Program template retrieved successfully', result);
    } catch (error) {
      handleControllerError(res, error);
    }
  }

  /**
   * Get templates by program type
   */
  @Get('program-type/:programTypeId')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Get all templates for a program type' })
  @ApiParam({ name: 'programTypeId', type: Number })
  @ApiResponse({ status: HttpStatus.OK, description: 'Templates retrieved successfully' })
  @ApiResponse({ status: HttpStatus.INTERNAL_SERVER_ERROR, description: 'Internal server error' })
  async findByProgramType(
    @Param('programTypeId', ParseIntPipe) programTypeId: number,
    @Res() res: Response,
  ) {
    this.logger.log('GET /v1/program-templates/program-type/:programTypeId - Request received', {
      programTypeId,
    });
    try {
      const result = await this.programTemplateService.findByProgramType(programTypeId);
      this.logger.log('Templates retrieved successfully');
      return this.responseService.success(res, 'Templates retrieved successfully', result);
    } catch (error) {
      handleControllerError(res, error);
    }
  }

  /**
   * Create a program template
   */
  @Post()
  @HttpCode(HttpStatus.CREATED)
  @ApiOperation({ summary: 'Create a new program template' })
  @ApiResponse({ status: HttpStatus.CREATED, description: 'Program template created successfully' })
  @ApiResponse({ status: HttpStatus.BAD_REQUEST, description: 'Invalid input data' })
  @ApiResponse({ status: HttpStatus.INTERNAL_SERVER_ERROR, description: 'Internal server error' })
  async create(@Body() createDto: CreateProgramTemplateDto, @Res() res: Response) {
    this.logger.log('POST /v1/program-templates - Request received', { createDto });
    try {
      const result = await this.programTemplateService.create(createDto);
      this.logger.log('Program template created successfully', { templateId: result.id });
      return this.responseService.success(
        res,
        'Program template created successfully',
        result,
        HttpStatus.CREATED,
      );
    } catch (error) {
      handleControllerError(res, error);
    }
  }

  /**
   * Update a program template
   */
  @Put(':id')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Update a program template' })
  @ApiParam({ name: 'id', type: Number })
  @ApiResponse({ status: HttpStatus.OK, description: 'Program template updated successfully' })
  @ApiResponse({ status: HttpStatus.NOT_FOUND, description: 'Program template not found' })
  @ApiResponse({ status: HttpStatus.BAD_REQUEST, description: 'Invalid input data' })
  @ApiResponse({ status: HttpStatus.INTERNAL_SERVER_ERROR, description: 'Internal server error' })
  async update(
    @Param('id', ParseIntPipe) id: number,
    @Body() updateDto: UpdateProgramTemplateDto,
    @Res() res: Response,
  ) {
    this.logger.log('PUT /v1/program-templates/:id - Request received', { id, updateDto });
    try {
      const result = await this.programTemplateService.update(id, updateDto);
      this.logger.log('Program template updated successfully', { templateId: id });
      return this.responseService.success(res, 'Program template updated successfully', result);
    } catch (error) {
      handleControllerError(res, error);
    }
  }

  /**
   * Publish a program template
   */
  @Post(':id/publish')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Publish a program template' })
  @ApiParam({ name: 'id', type: Number })
  @ApiResponse({ status: HttpStatus.OK, description: 'Program template published successfully' })
  @ApiResponse({ status: HttpStatus.NOT_FOUND, description: 'Program template not found' })
  @ApiResponse({ status: HttpStatus.BAD_REQUEST, description: 'Template cannot be published' })
  @ApiResponse({ status: HttpStatus.INTERNAL_SERVER_ERROR, description: 'Internal server error' })
  async publish(@Param('id', ParseIntPipe) id: number, @Res() res: Response) {
    this.logger.log('POST /v1/program-templates/:id/publish - Request received', { id });
    try {
      const result = await this.programTemplateService.publish(id);
      this.logger.log('Program template published successfully', { templateId: id });
      return this.responseService.success(res, 'Program template published successfully', result);
    } catch (error) {
      handleControllerError(res, error);
    }
  }

  /**
   * Archive a program template
   */
  @Post(':id/archive')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Archive a program template' })
  @ApiParam({ name: 'id', type: Number })
  @ApiResponse({ status: HttpStatus.OK, description: 'Program template archived successfully' })
  @ApiResponse({ status: HttpStatus.NOT_FOUND, description: 'Program template not found' })
  @ApiResponse({ status: HttpStatus.INTERNAL_SERVER_ERROR, description: 'Internal server error' })
  async archive(@Param('id', ParseIntPipe) id: number, @Res() res: Response) {
    this.logger.log('POST /v1/program-templates/:id/archive - Request received', { id });
    try {
      const result = await this.programTemplateService.archive(id);
      this.logger.log('Program template archived successfully', { templateId: id });
      return this.responseService.success(res, 'Program template archived successfully', result);
    } catch (error) {
      handleControllerError(res, error);
    }
  }

  /**
   * Delete a program template
   */
  @Delete(':id')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Delete a program template (soft delete)' })
  @ApiParam({ name: 'id', type: Number })
  @ApiResponse({ status: HttpStatus.OK, description: 'Program template deleted successfully' })
  @ApiResponse({ status: HttpStatus.NOT_FOUND, description: 'Program template not found' })
  @ApiResponse({ status: HttpStatus.BAD_REQUEST, description: 'Cannot delete published template' })
  @ApiResponse({ status: HttpStatus.INTERNAL_SERVER_ERROR, description: 'Internal server error' })
  async delete(@Param('id', ParseIntPipe) id: number, @Res() res: Response) {
    this.logger.log('DELETE /v1/program-templates/:id - Request received', { id });
    try {
      const result = await this.programTemplateService.delete(id);
      this.logger.log('Program template deleted successfully', { templateId: id });
      return this.responseService.success(res, 'Program template deleted successfully', result);
    } catch (error) {
      handleControllerError(res, error);
    }
  }

  /**
   * Clone master sections to template
   */
  @Post(':id/clone-from-master')
  @HttpCode(HttpStatus.CREATED)
  @ApiOperation({ summary: 'Clone master form section(s) to this program template' })
  @ApiParam({ name: 'id', description: 'Program template ID', type: Number })
  @ApiResponse({ status: HttpStatus.CREATED, description: 'Section(s) cloned successfully' })
  @ApiResponse({ status: HttpStatus.BAD_REQUEST, description: 'Invalid input or already cloned' })
  @ApiResponse({ status: HttpStatus.NOT_FOUND, description: 'Template or master section not found' })
  @ApiResponse({ status: HttpStatus.INTERNAL_SERVER_ERROR, description: 'Internal server error' })
  async cloneFromMaster(
    @Param('id', ParseIntPipe) templateId: number,
    @Body() cloneDto: CloneFromMasterDto,
    @Res() res: Response,
  ) {
    this.logger.log('POST /v1/program-templates/:id/clone-from-master - Request received', {
      templateId,
      cloneDto,
    });
    try {
      // Set programTemplateId from route parameter
      cloneDto.programTemplateId = templateId;
      const result = await this.templateFormSectionService.cloneFromMaster(cloneDto);
      this.logger.log('Section(s) cloned from master successfully', {
        templateId,
        clonedCount: result.clonedSections,
      });
      return this.responseService.success(
        res,
        'Section(s) cloned from master successfully',
        result,
        HttpStatus.CREATED,
      );
    } catch (error) {
      handleControllerError(res, error);
    }
  }

  /**
   * Build template form (unified endpoint for cloning and creating sections/questions)
   */
  @Post(':id/form')
  @HttpCode(HttpStatus.CREATED)
  @ApiOperation({
    summary: 'Build template form by cloning from master and/or creating new sections/questions',
    description: `
      Unified endpoint for building template forms with maximum flexibility:
      - Clone sections from master with optional overrides
      - Create brand new custom sections
      - Clone questions from master with optional overrides
      - Create brand new custom questions
      - Mix cloned and custom content in same request
      - Each section MUST specify its questions array (explicit control)
      - Validates nesting depth against MAX_SECTION_NESTING_DEPTH constant
      
      Supports all cases from simple cloning to complex custom forms.
    `,
  })
  @ApiParam({ name: 'id', description: 'Program template ID', type: Number })
  @ApiResponse({
    status: HttpStatus.CREATED,
    description: 'Template form built successfully',
    type: TemplateFormBuilderResponseDto,
  })
  @ApiResponse({ status: HttpStatus.BAD_REQUEST, description: 'Invalid input - check validation errors' })
  @ApiResponse({ status: HttpStatus.NOT_FOUND, description: 'Template, master section or question not found' })
  @ApiResponse({ status: HttpStatus.INTERNAL_SERVER_ERROR, description: 'Internal server error' })
  async buildTemplateForm(
    @Param('id', ParseIntPipe) templateId: number,
    @Body() dto: TemplateFormBuilderDto,
    @Res() res: Response,
  ) {
    this.logger.log('POST /v1/program-templates/:id/form - Request received', { templateId, dto });
    try {
      // Set programTemplateId from route parameter
      dto.programTemplateId = templateId;
      const result = await this.templateFormSectionService.buildTemplateForm(dto);
      this.logger.log('Template form built successfully', {
        templateId,
        sectionsCreated: result.data.totalSectionsCreated,
        questionsCreated: result.data.totalQuestionsCreated,
      });
      return this.responseService.success(res, result.message, result.data, HttpStatus.CREATED);
    } catch (error) {
      handleControllerError(res, error);
    }
  }

  /**
   * Update template form structure (Option A: Direct template update)
   */
  @Patch(':id/form')
  @HttpCode(HttpStatus.OK)
  @UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
  @ApiOperation({
    summary: 'Update template form structure (sections and questions)',
    description: `
      Updates an existing template form structure (OPTION A workflow).
      Supports:
      - Deleting entire sections
      - Updating section metadata and questions
      - Adding new sections with questions
      
      This is the direct template update workflow. Changes affect the template only.
      Use POST /program/:programId/sync-from-template to copy changes to a specific program.
    `,
  })
  @ApiParam({ name: 'id', description: 'Program template ID', type: Number })
  @ApiBody({ type: UpdateTemplateFormDto })
  @ApiResponse({
    status: HttpStatus.OK,
    description: 'Template form updated successfully',
    schema: {
      example: {
        success: true,
        message: 'Template form updated successfully',
        data: {
          programTemplateId: 5,
          sectionsDeleted: 1,
          sectionsUpdated: 2,
          sectionsAdded: 1,
          questionsDeleted: 3,
          questionsUpdated: 5,
          questionsAdded: 4,
        },
      },
    },
  })
  @ApiResponse({ status: HttpStatus.BAD_REQUEST, description: 'Invalid input data' })
  @ApiResponse({ status: HttpStatus.NOT_FOUND, description: 'Template or section/question not found' })
  @ApiResponse({ status: HttpStatus.UNAUTHORIZED, description: 'Unauthorized - user authentication required' })
  @ApiResponse({ status: HttpStatus.INTERNAL_SERVER_ERROR, description: 'Internal server error' })
  async updateTemplateForm(
    @Param('id', ParseIntPipe) templateId: number,
    @Body() dto: UpdateTemplateFormDto,
    @Req() req: any,
    @Res() res: Response,
  ) {
    this.logger.log('PATCH /v1/program-templates/:id/form - Request received', { templateId, dto });
    try {
      const userId = req.user?.id;
      if (!userId) {
        return this.errorHandler.unauthorized(res);
      }

      // Set programTemplateId from route parameter
      dto.programTemplateId = templateId;

      const result = (await this.programTemplateService.updateTemplateForm(dto, userId))!;
      this.logger.log('Template form updated successfully', result.data);
      return this.responseService.success(res, result.message, result.data, HttpStatus.OK);
    } catch (error) {
      handleControllerError(res, error);
    }
  }
}
