import { FastifyReply, FastifyRequest } from 'fastify';
import { MaintenanceService } from '../domain/maintenance.service';
import { logger } from '../../../core/logging/logger';
import {
  CreateMaintenanceRecordSchema,
  UpdateMaintenanceRecordSchema,
  CreateScheduleSchema,
  UpdateScheduleSchema,
  getSubtypesForCategory,
  MaintenanceCategory
} from '../domain/maintenance.types';
import { z } from 'zod';

export class MaintenanceController {
  private readonly service = new MaintenanceService();

  async listRecords(
    request: FastifyRequest<{ Querystring: { vehicleId?: string; category?: string } }>,
    reply: FastifyReply
  ) {
    const userId = (request as any).user?.sub as string;

    logger.info('Maintenance records list requested', {
      operation: 'maintenance.records.list',
      userId,
      filters: {
        vehicleId: request.query.vehicleId,
        category: request.query.category,
      },
    });

    try {
      const filters: { vehicleId?: string; category?: MaintenanceCategory } = {};
      if (request.query.vehicleId) {
        filters.vehicleId = request.query.vehicleId;
      }
      if (request.query.category) {
        filters.category = request.query.category as MaintenanceCategory;
      }

      const records = await this.service.getRecords(userId, filters);

      logger.info('Maintenance records list retrieved', {
        operation: 'maintenance.records.list.success',
        userId,
        recordCount: records.length,
      });

      return reply.code(200).send(records);
    } catch (error) {
      logger.error('Failed to list maintenance records', {
        operation: 'maintenance.records.list.error',
        userId,
        error: error instanceof Error ? error.message : 'Unknown error',
      });
      throw error;
    }
  }

  async getRecord(request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) {
    const userId = (request as any).user?.sub as string;
    const recordId = request.params.id;

    logger.info('Maintenance record get requested', {
      operation: 'maintenance.records.get',
      userId,
      recordId,
    });

    try {
      const record = await this.service.getRecord(userId, recordId);
      if (!record) {
        logger.warn('Maintenance record not found', {
          operation: 'maintenance.records.get.not_found',
          userId,
          recordId,
        });
        return reply.code(404).send({ error: 'Not Found' });
      }

      logger.info('Maintenance record retrieved', {
        operation: 'maintenance.records.get.success',
        userId,
        recordId,
        vehicleId: record.vehicleId,
        category: record.category,
      });

      return reply.code(200).send(record);
    } catch (error) {
      logger.error('Failed to get maintenance record', {
        operation: 'maintenance.records.get.error',
        userId,
        recordId,
        error: error instanceof Error ? error.message : 'Unknown error',
      });
      throw error;
    }
  }

  async getRecordsByVehicle(
    request: FastifyRequest<{ Params: { vehicleId: string } }>,
    reply: FastifyReply
  ) {
    const userId = (request as any).user?.sub as string;
    const vehicleId = request.params.vehicleId;

    logger.info('Maintenance records by vehicle requested', {
      operation: 'maintenance.records.by_vehicle',
      userId,
      vehicleId,
    });

    try {
      const records = await this.service.getRecordsByVehicle(userId, vehicleId);

      logger.info('Maintenance records by vehicle retrieved', {
        operation: 'maintenance.records.by_vehicle.success',
        userId,
        vehicleId,
        recordCount: records.length,
      });

      return reply.code(200).send(records);
    } catch (error) {
      logger.error('Failed to get maintenance records by vehicle', {
        operation: 'maintenance.records.by_vehicle.error',
        userId,
        vehicleId,
        error: error instanceof Error ? error.message : 'Unknown error',
      });
      throw error;
    }
  }

  async createRecord(request: FastifyRequest<{ Body: unknown }>, reply: FastifyReply) {
    const userId = (request as any).user?.sub as string;

    logger.info('Maintenance record create requested', {
      operation: 'maintenance.records.create',
      userId,
    });

    try {
      const validated = CreateMaintenanceRecordSchema.parse(request.body);

      const record = await this.service.createRecord(userId, validated);

      logger.info('Maintenance record created', {
        operation: 'maintenance.records.create.success',
        userId,
        recordId: record.id,
        vehicleId: record.vehicleId,
        category: record.category,
        subtypeCount: record.subtypes.length,
      });

      return reply.code(201).send(record);
    } catch (error) {
      if (error instanceof z.ZodError) {
        logger.warn('Maintenance record validation failed', {
          operation: 'maintenance.records.create.validation_error',
          userId,
          errors: error.errors,
        });
        return reply.code(400).send({ error: 'Bad Request', details: error.errors });
      }

      if (error instanceof Error && 'statusCode' in error) {
        const statusCode = (error as any).statusCode;
        logger.warn('Maintenance record creation failed', {
          operation: 'maintenance.records.create.error',
          userId,
          statusCode,
          error: error.message,
        });
        return reply.code(statusCode).send({ error: error.message });
      }

      logger.error('Failed to create maintenance record', {
        operation: 'maintenance.records.create.error',
        userId,
        error: error instanceof Error ? error.message : 'Unknown error',
      });
      throw error;
    }
  }

  async updateRecord(
    request: FastifyRequest<{ Params: { id: string }; Body: unknown }>,
    reply: FastifyReply
  ) {
    const userId = (request as any).user?.sub as string;
    const recordId = request.params.id;

    logger.info('Maintenance record update requested', {
      operation: 'maintenance.records.update',
      userId,
      recordId,
    });

    try {
      const validated = UpdateMaintenanceRecordSchema.parse(request.body);

      const record = await this.service.updateRecord(userId, recordId, validated);
      if (!record) {
        logger.warn('Maintenance record not found for update', {
          operation: 'maintenance.records.update.not_found',
          userId,
          recordId,
        });
        return reply.code(404).send({ error: 'Not Found' });
      }

      logger.info('Maintenance record updated', {
        operation: 'maintenance.records.update.success',
        userId,
        recordId,
        vehicleId: record.vehicleId,
        category: record.category,
      });

      return reply.code(200).send(record);
    } catch (error) {
      if (error instanceof z.ZodError) {
        logger.warn('Maintenance record update validation failed', {
          operation: 'maintenance.records.update.validation_error',
          userId,
          recordId,
          errors: error.errors,
        });
        return reply.code(400).send({ error: 'Bad Request', details: error.errors });
      }

      if (error instanceof Error && 'statusCode' in error) {
        const statusCode = (error as any).statusCode;
        logger.warn('Maintenance record update failed', {
          operation: 'maintenance.records.update.error',
          userId,
          recordId,
          statusCode,
          error: error.message,
        });
        return reply.code(statusCode).send({ error: error.message });
      }

      logger.error('Failed to update maintenance record', {
        operation: 'maintenance.records.update.error',
        userId,
        recordId,
        error: error instanceof Error ? error.message : 'Unknown error',
      });
      throw error;
    }
  }

  async deleteRecord(request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) {
    const userId = (request as any).user?.sub as string;
    const recordId = request.params.id;

    logger.info('Maintenance record delete requested', {
      operation: 'maintenance.records.delete',
      userId,
      recordId,
    });

    try {
      await this.service.deleteRecord(userId, recordId);

      logger.info('Maintenance record deleted', {
        operation: 'maintenance.records.delete.success',
        userId,
        recordId,
      });

      return reply.code(204).send();
    } catch (error) {
      logger.error('Failed to delete maintenance record', {
        operation: 'maintenance.records.delete.error',
        userId,
        recordId,
        error: error instanceof Error ? error.message : 'Unknown error',
      });
      throw error;
    }
  }

  async getSchedulesByVehicle(
    request: FastifyRequest<{ Params: { vehicleId: string } }>,
    reply: FastifyReply
  ) {
    const userId = (request as any).user?.sub as string;
    const vehicleId = request.params.vehicleId;

    logger.info('Maintenance schedules by vehicle requested', {
      operation: 'maintenance.schedules.by_vehicle',
      userId,
      vehicleId,
    });

    try {
      const schedules = await this.service.getSchedulesByVehicle(userId, vehicleId);

      logger.info('Maintenance schedules by vehicle retrieved', {
        operation: 'maintenance.schedules.by_vehicle.success',
        userId,
        vehicleId,
        scheduleCount: schedules.length,
      });

      return reply.code(200).send(schedules);
    } catch (error) {
      logger.error('Failed to get maintenance schedules by vehicle', {
        operation: 'maintenance.schedules.by_vehicle.error',
        userId,
        vehicleId,
        error: error instanceof Error ? error.message : 'Unknown error',
      });
      throw error;
    }
  }

  async createSchedule(request: FastifyRequest<{ Body: unknown }>, reply: FastifyReply) {
    const userId = (request as any).user?.sub as string;

    logger.info('Maintenance schedule create requested', {
      operation: 'maintenance.schedules.create',
      userId,
    });

    try {
      const validated = CreateScheduleSchema.parse(request.body);

      const schedule = await this.service.createSchedule(userId, validated);

      logger.info('Maintenance schedule created', {
        operation: 'maintenance.schedules.create.success',
        userId,
        scheduleId: schedule.id,
        vehicleId: schedule.vehicleId,
        category: schedule.category,
        subtypeCount: schedule.subtypes.length,
      });

      return reply.code(201).send(schedule);
    } catch (error) {
      if (error instanceof z.ZodError) {
        logger.warn('Maintenance schedule validation failed', {
          operation: 'maintenance.schedules.create.validation_error',
          userId,
          errors: error.errors,
        });
        return reply.code(400).send({ error: 'Bad Request', details: error.errors });
      }

      if (error instanceof Error && 'statusCode' in error) {
        const statusCode = (error as any).statusCode;
        logger.warn('Maintenance schedule creation failed', {
          operation: 'maintenance.schedules.create.error',
          userId,
          statusCode,
          error: error.message,
        });
        return reply.code(statusCode).send({ error: error.message });
      }

      logger.error('Failed to create maintenance schedule', {
        operation: 'maintenance.schedules.create.error',
        userId,
        error: error instanceof Error ? error.message : 'Unknown error',
      });
      throw error;
    }
  }

  async updateSchedule(
    request: FastifyRequest<{ Params: { id: string }; Body: unknown }>,
    reply: FastifyReply
  ) {
    const userId = (request as any).user?.sub as string;
    const scheduleId = request.params.id;

    logger.info('Maintenance schedule update requested', {
      operation: 'maintenance.schedules.update',
      userId,
      scheduleId,
    });

    try {
      const validated = UpdateScheduleSchema.parse(request.body);

      const schedule = await this.service.updateSchedule(userId, scheduleId, validated);
      if (!schedule) {
        logger.warn('Maintenance schedule not found for update', {
          operation: 'maintenance.schedules.update.not_found',
          userId,
          scheduleId,
        });
        return reply.code(404).send({ error: 'Not Found' });
      }

      logger.info('Maintenance schedule updated', {
        operation: 'maintenance.schedules.update.success',
        userId,
        scheduleId,
        vehicleId: schedule.vehicleId,
        category: schedule.category,
      });

      return reply.code(200).send(schedule);
    } catch (error) {
      if (error instanceof z.ZodError) {
        logger.warn('Maintenance schedule update validation failed', {
          operation: 'maintenance.schedules.update.validation_error',
          userId,
          scheduleId,
          errors: error.errors,
        });
        return reply.code(400).send({ error: 'Bad Request', details: error.errors });
      }

      if (error instanceof Error && 'statusCode' in error) {
        const statusCode = (error as any).statusCode;
        logger.warn('Maintenance schedule update failed', {
          operation: 'maintenance.schedules.update.error',
          userId,
          scheduleId,
          statusCode,
          error: error.message,
        });
        return reply.code(statusCode).send({ error: error.message });
      }

      logger.error('Failed to update maintenance schedule', {
        operation: 'maintenance.schedules.update.error',
        userId,
        scheduleId,
        error: error instanceof Error ? error.message : 'Unknown error',
      });
      throw error;
    }
  }

  async deleteSchedule(request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) {
    const userId = (request as any).user?.sub as string;
    const scheduleId = request.params.id;

    logger.info('Maintenance schedule delete requested', {
      operation: 'maintenance.schedules.delete',
      userId,
      scheduleId,
    });

    try {
      await this.service.deleteSchedule(userId, scheduleId);

      logger.info('Maintenance schedule deleted', {
        operation: 'maintenance.schedules.delete.success',
        userId,
        scheduleId,
      });

      return reply.code(204).send();
    } catch (error) {
      logger.error('Failed to delete maintenance schedule', {
        operation: 'maintenance.schedules.delete.error',
        userId,
        scheduleId,
        error: error instanceof Error ? error.message : 'Unknown error',
      });
      throw error;
    }
  }

  async getUpcoming(
    request: FastifyRequest<{ Params: { vehicleId: string }; Querystring: { currentMileage?: string } }>,
    reply: FastifyReply
  ) {
    const userId = (request as any).user?.sub as string;
    const vehicleId = request.params.vehicleId;
    const currentMileage = request.query.currentMileage ? parseInt(request.query.currentMileage, 10) : undefined;

    logger.info('Upcoming maintenance requested', {
      operation: 'maintenance.upcoming',
      userId,
      vehicleId,
      currentMileage,
    });

    try {
      const upcoming = await this.service.getUpcomingMaintenance(userId, vehicleId, currentMileage);

      logger.info('Upcoming maintenance retrieved', {
        operation: 'maintenance.upcoming.success',
        userId,
        vehicleId,
        upcomingCount: upcoming.length,
      });

      return reply.code(200).send(upcoming);
    } catch (error) {
      logger.error('Failed to get upcoming maintenance', {
        operation: 'maintenance.upcoming.error',
        userId,
        vehicleId,
        error: error instanceof Error ? error.message : 'Unknown error',
      });
      throw error;
    }
  }

  async getSubtypes(request: FastifyRequest<{ Params: { category: string } }>, reply: FastifyReply) {
    const userId = (request as any).user?.sub as string;
    const category = request.params.category;

    logger.info('Maintenance subtypes requested', {
      operation: 'maintenance.subtypes',
      userId,
      category,
    });

    try {
      if (!['routine_maintenance', 'repair', 'performance_upgrade'].includes(category)) {
        logger.warn('Invalid maintenance category', {
          operation: 'maintenance.subtypes.invalid_category',
          userId,
          category,
        });
        return reply.code(400).send({ error: 'Bad Request', message: 'Invalid category' });
      }

      const subtypes = getSubtypesForCategory(category as MaintenanceCategory);

      logger.info('Maintenance subtypes retrieved', {
        operation: 'maintenance.subtypes.success',
        userId,
        category,
        subtypeCount: subtypes.length,
      });

      return reply.code(200).send({ category, subtypes: Array.from(subtypes) });
    } catch (error) {
      logger.error('Failed to get maintenance subtypes', {
        operation: 'maintenance.subtypes.error',
        userId,
        category,
        error: error instanceof Error ? error.message : 'Unknown error',
      });
      throw error;
    }
  }
}
