import { Request, Response, NextFunction, Router } from 'express';
import { ProfilesService } from '../services/profiles.service';
import { CreateProfileDto, UpdateProfileDto, AssignProfileDto, CreateProfileToolDto } from '../dto';

export class ProfilesController {
  public router: Router;

  constructor(private readonly profilesService: ProfilesService) {
    this.router = Router();
    this.initializeRoutes();
  }

  private initializeRoutes(): void {
    // Profiles CRUD
    this.router.get('/', this.findAll.bind(this));
    this.router.get('/:id', this.findOne.bind(this));
    this.router.get('/code/:code', this.findByCode.bind(this));
    this.router.post('/', this.create.bind(this));
    this.router.patch('/:id', this.update.bind(this));
    this.router.delete('/:id', this.delete.bind(this));

    // Profile Tools
    this.router.get('/:id/tools', this.getTools.bind(this));
    this.router.post('/:id/tools', this.addTool.bind(this));
    this.router.delete('/:id/tools/:toolCode', this.removeTool.bind(this));

    // User Profile Assignments
    this.router.post('/assign', this.assignProfile.bind(this));
    this.router.delete('/assign/:userId/:profileId', this.unassignProfile.bind(this));
    this.router.get('/user/:userId', this.getUserProfiles.bind(this));
    this.router.get('/user/:userId/primary', this.getPrimaryProfile.bind(this));
    this.router.get('/user/:userId/tools', this.getUserTools.bind(this));

    // Permissions Check
    this.router.get('/user/:userId/module/:moduleCode/access', this.checkModuleAccess.bind(this));
    this.router.get('/user/:userId/platform/:platform/access', this.checkPlatformAccess.bind(this));
  }

  // ============================================
  // PROFILES CRUD
  // ============================================

  private async findAll(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const profiles = await this.profilesService.findAll(tenantId);
      res.json({ data: profiles, total: profiles.length });
    } catch (error) {
      next(error);
    }
  }

  private async findOne(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const profile = await this.profilesService.findOne(id);

      if (!profile) {
        res.status(404).json({ error: 'Profile not found' });
        return;
      }

      res.json({ data: profile });
    } catch (error) {
      next(error);
    }
  }

  private async findByCode(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { code } = req.params;
      const tenantId = req.headers['x-tenant-id'] as string;
      const profile = await this.profilesService.findByCode(code, tenantId);

      if (!profile) {
        res.status(404).json({ error: 'Profile not found' });
        return;
      }

      res.json({ data: profile });
    } catch (error) {
      next(error);
    }
  }

  private async create(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const userId = req.headers['x-user-id'] as string;
      const dto: CreateProfileDto = req.body;

      const profile = await this.profilesService.create(tenantId, dto, userId);
      res.status(201).json({ data: profile });
    } catch (error) {
      next(error);
    }
  }

  private async update(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const userId = req.headers['x-user-id'] as string;
      const dto: UpdateProfileDto = req.body;

      const profile = await this.profilesService.update(id, dto, userId);

      if (!profile) {
        res.status(404).json({ error: 'Profile not found' });
        return;
      }

      res.json({ data: profile });
    } catch (error) {
      next(error);
    }
  }

  private async delete(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const deleted = await this.profilesService.delete(id);

      if (!deleted) {
        res.status(404).json({ error: 'Profile not found' });
        return;
      }

      res.status(204).send();
    } catch (error) {
      next(error);
    }
  }

  // ============================================
  // PROFILE TOOLS
  // ============================================

  private async getTools(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const tools = await this.profilesService.getToolsForProfile(id);
      res.json({ data: tools });
    } catch (error) {
      next(error);
    }
  }

  private async addTool(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const dto: CreateProfileToolDto = req.body;

      const tool = await this.profilesService.addTool(id, dto);
      res.status(201).json({ data: tool });
    } catch (error) {
      next(error);
    }
  }

  private async removeTool(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id, toolCode } = req.params;
      const removed = await this.profilesService.removeTool(id, toolCode);

      if (!removed) {
        res.status(404).json({ error: 'Tool not found' });
        return;
      }

      res.status(204).send();
    } catch (error) {
      next(error);
    }
  }

  // ============================================
  // USER PROFILE ASSIGNMENTS
  // ============================================

  private async assignProfile(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const userId = req.headers['x-user-id'] as string;
      const dto: AssignProfileDto = req.body;

      const assignment = await this.profilesService.assignProfile(dto, userId);
      res.status(201).json({ data: assignment });
    } catch (error) {
      next(error);
    }
  }

  private async unassignProfile(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { userId, profileId } = req.params;
      const unassigned = await this.profilesService.unassignProfile(userId, profileId);

      if (!unassigned) {
        res.status(404).json({ error: 'Assignment not found' });
        return;
      }

      res.status(204).send();
    } catch (error) {
      next(error);
    }
  }

  private async getUserProfiles(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { userId } = req.params;
      const profiles = await this.profilesService.getUserProfiles(userId);
      res.json({ data: profiles });
    } catch (error) {
      next(error);
    }
  }

  private async getPrimaryProfile(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { userId } = req.params;
      const profile = await this.profilesService.getPrimaryProfile(userId);

      if (!profile) {
        res.status(404).json({ error: 'No primary profile found' });
        return;
      }

      res.json({ data: profile });
    } catch (error) {
      next(error);
    }
  }

  private async getUserTools(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { userId } = req.params;
      const platform = req.query.platform as string | undefined;
      const tools = await this.profilesService.getToolsForUser(userId, platform);
      res.json({ data: tools });
    } catch (error) {
      next(error);
    }
  }

  // ============================================
  // PERMISSIONS CHECK
  // ============================================

  private async checkModuleAccess(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { userId, moduleCode } = req.params;
      const hasAccess = await this.profilesService.hasModuleAccess(userId, moduleCode);
      const accessLevel = await this.profilesService.getModuleAccessLevel(userId, moduleCode);

      res.json({
        data: {
          hasAccess,
          accessLevel,
        },
      });
    } catch (error) {
      next(error);
    }
  }

  private async checkPlatformAccess(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { userId, platform } = req.params;
      const hasAccess = await this.profilesService.hasPlatformAccess(userId, platform);

      res.json({
        data: {
          hasAccess,
          platform,
        },
      });
    } catch (error) {
      next(error);
    }
  }
}
