import { Router, Request, Response, NextFunction } from 'express';
import { authenticate } from '../../middleware/auth.middleware';
import { requireRole } from '../../middleware/rbac.middleware';
import { validate } from '../../middleware/validate';
import { ADMIN_ROLES, INFLUENCE_ROLES, MAP_ROLES } from '../../utils/roles';
import { prisma } from '../../config/database';
import { peopleService } from './people.service';
import { profileService } from './profile.service';
import { tagsService } from './tags.service';
import {
  listPeopleSchema,
  managePersonSchema,
  createContactSchema,
  updateContactSchema,
  createConnectionSchema,
  mergeContactSchema,
  graphQuerySchema,
  activityListSchema,
  createUserFromContactSchema,
  addContactAddressSchema,
  addContactEmailSchema,
  addContactPhoneSchema,
  generateProfileLinkSchema,
  updateProfileLinkSchema,
} from './people.schemas';
import {
  createTagSchema,
  updateTagSchema,
  deleteTagQuerySchema,
  bulkTagSchema,
} from './tags.schemas';

const router = Router();

// All routes require admin auth
router.use(authenticate, requireRole(...ADMIN_ROLES));

// ---------------------------------------------------------------------------
// List & Search
// ---------------------------------------------------------------------------

// GET /api/people — list/search all virtual people
router.get(
  '/',
  validate(listPeopleSchema, 'query'),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const result = await peopleService.listPeople(req.query as any);
      res.json(result);
    } catch (err) {
      next(err);
    }
  }
);

// GET /api/people/stats — aggregate stats
router.get(
  '/stats',
  async (_req: Request, res: Response, next: NextFunction) => {
    try {
      const stats = await peopleService.getStats();
      res.json(stats);
    } catch (err) {
      next(err);
    }
  }
);

// GET /api/people/duplicates — find potential duplicates
router.get(
  '/duplicates',
  async (_req: Request, res: Response, next: NextFunction) => {
    try {
      const duplicates = await peopleService.getDuplicates();
      res.json({ duplicates });
    } catch (err) {
      next(err);
    }
  }
);

// ---------------------------------------------------------------------------
// Graph
// ---------------------------------------------------------------------------

// GET /api/people/graph — build graph nodes + edges
router.get(
  '/graph',
  validate(graphQuerySchema, 'query'),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const result = await peopleService.getGraphData(req.query as any);
      res.json(result);
    } catch (err) {
      next(err);
    }
  }
);

// ---------------------------------------------------------------------------
// Household
// ---------------------------------------------------------------------------

// Household routes tightened 2026-04-12: contain full-address PII (names,
// emails, phones at a specific address). Previously accessible to any ADMIN_ROLE;
// now restricted to SUPER_ADMIN + INFLUENCE_ADMIN + MAP_ADMIN (the roles that
// legitimately handle location-based contact data). MEDIA/BROADCAST/etc. admins
// have no business viewing household PII.
const HOUSEHOLD_ROLES = Array.from(new Set([...INFLUENCE_ROLES, ...MAP_ROLES]));

// GET /api/people/household/:locationId — all people at a location
router.get(
  '/household/:locationId',
  requireRole(...HOUSEHOLD_ROLES),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const locationId = req.params.locationId as string;
      const result = await peopleService.getHousehold(locationId);
      res.json(result);
    } catch (err) {
      next(err);
    }
  }
);

// POST /api/people/household/:locationId/detect — auto-create HOUSEHOLD connections
router.post(
  '/household/:locationId/detect',
  requireRole(...HOUSEHOLD_ROLES),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const locationId = req.params.locationId as string;
      const result = await peopleService.detectHousehold(locationId);
      res.json(result);
    } catch (err) {
      next(err);
    }
  }
);

// ---------------------------------------------------------------------------
// CRM Management
// ---------------------------------------------------------------------------

// POST /api/people/manage — promote a virtual person to managed Contact
router.post(
  '/manage',
  validate(managePersonSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const contact = await peopleService.manageContact(req.body, req.user!.id);
      res.status(201).json(contact);
    } catch (err) {
      next(err);
    }
  }
);

// POST /api/people/contacts — create a new Contact directly (manual add)
router.post(
  '/contacts',
  validate(createContactSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const contact = await peopleService.createContact(req.body, req.user!.id);
      res.status(201).json(contact);
    } catch (err) {
      next(err);
    }
  }
);

// PUT /api/people/contacts/:id — update Contact CRM fields
router.put(
  '/contacts/:id',
  validate(updateContactSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const contact = await peopleService.updateContact(id, req.body);
      res.json(contact);
    } catch (err) {
      next(err);
    }
  }
);

// DELETE /api/people/contacts/:id — delete a Contact (virtual person still visible)
router.delete(
  '/contacts/:id',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      await peopleService.deleteContact(id);
      res.status(204).send();
    } catch (err) {
      next(err);
    }
  }
);

// POST /api/people/contacts/:id/merge — merge another person into this Contact
router.post(
  '/contacts/:id/merge',
  validate(mergeContactSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const result = await peopleService.mergeContacts(id, req.body);
      res.json(result);
    } catch (err) {
      next(err);
    }
  }
);

// ---------------------------------------------------------------------------
// Profile Link Management
// ---------------------------------------------------------------------------

// POST /api/people/contacts/:id/profile-link — Generate profile token (with optional expiration/password)
router.post(
  '/contacts/:id/profile-link',
  validate(generateProfileLinkSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const result = await profileService.generateProfileToken(id, req.body);
      res.json(result);
    } catch (err) {
      next(err);
    }
  }
);

// PUT /api/people/contacts/:id/profile-link — Update settings on existing link (no token regeneration)
router.put(
  '/contacts/:id/profile-link',
  validate(updateProfileLinkSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const result = await profileService.updateProfileLinkSettings(id, req.body);
      res.json(result);
    } catch (err) {
      if (err instanceof Error && err.message.includes('not found or has no profile link')) {
        res.status(404).json({ error: { message: err.message, code: 'NOT_FOUND' } });
        return;
      }
      next(err);
    }
  }
);

// DELETE /api/people/contacts/:id/profile-link — Revoke profile token
router.delete(
  '/contacts/:id/profile-link',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      await profileService.revokeProfileToken(id);
      res.status(204).send();
    } catch (err) {
      next(err);
    }
  }
);

// POST /api/people/contacts/:id/send-profile-link — Send profile link via email
router.post(
  '/contacts/:id/send-profile-link',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const result = await profileService.sendProfileLink(id, req.user!.id);
      res.json(result);
    } catch (err) {
      next(err);
    }
  }
);

// ---------------------------------------------------------------------------
// Contact Addresses (Map Integration)
// ---------------------------------------------------------------------------

// GET /api/people/contacts/:id/addresses — list linked addresses
router.get(
  '/contacts/:id/addresses',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const addresses = await peopleService.getAddresses(id);
      res.json({ addresses });
    } catch (err) {
      next(err);
    }
  }
);

// POST /api/people/contacts/:id/addresses — add address (with map sync)
router.post(
  '/contacts/:id/addresses',
  validate(addContactAddressSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const result = await peopleService.addAddress(id, req.body, req.user!.id);
      res.status(201).json(result);
    } catch (err) {
      next(err);
    }
  }
);

// DELETE /api/people/contacts/:id/addresses/:linkId — unlink address
router.delete(
  '/contacts/:id/addresses/:linkId',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const linkId = req.params.linkId as string;
      await peopleService.removeAddress(id, linkId);
      res.status(204).send();
    } catch (err) {
      next(err);
    }
  }
);

// ---------------------------------------------------------------------------
// Contact Emails
// ---------------------------------------------------------------------------

// GET /api/people/contacts/:id/emails — list emails for a contact
router.get(
  '/contacts/:id/emails',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const emails = await peopleService.getEmails(id);
      res.json({ emails });
    } catch (err) {
      next(err);
    }
  }
);

// POST /api/people/contacts/:id/emails — add email to a contact
router.post(
  '/contacts/:id/emails',
  validate(addContactEmailSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const emails = await peopleService.addEmail(id, req.body);
      res.status(201).json({ emails });
    } catch (err) {
      next(err);
    }
  }
);

// PUT /api/people/contacts/:id/emails/:emailId/primary — set primary email
router.put(
  '/contacts/:id/emails/:emailId/primary',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const emailId = req.params.emailId as string;
      await peopleService.setPrimaryEmail(id, emailId);
      res.json({ success: true });
    } catch (err) {
      next(err);
    }
  }
);

// DELETE /api/people/contacts/:id/emails/:emailId — remove email
router.delete(
  '/contacts/:id/emails/:emailId',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const emailId = req.params.emailId as string;
      await peopleService.removeEmail(id, emailId);
      res.status(204).send();
    } catch (err) {
      next(err);
    }
  }
);

// ---------------------------------------------------------------------------
// Contact Phones
// ---------------------------------------------------------------------------

// GET /api/people/contacts/:id/phones — list phones for a contact
router.get(
  '/contacts/:id/phones',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const phones = await peopleService.getPhones(id);
      res.json({ phones });
    } catch (err) {
      next(err);
    }
  }
);

// POST /api/people/contacts/:id/phones — add phone to a contact
router.post(
  '/contacts/:id/phones',
  validate(addContactPhoneSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const phones = await peopleService.addPhone(id, req.body);
      res.status(201).json({ phones });
    } catch (err) {
      next(err);
    }
  }
);

// PUT /api/people/contacts/:id/phones/:phoneId/primary — set primary phone
router.put(
  '/contacts/:id/phones/:phoneId/primary',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const phoneId = req.params.phoneId as string;
      await peopleService.setPrimaryPhone(id, phoneId);
      res.json({ success: true });
    } catch (err) {
      next(err);
    }
  }
);

// DELETE /api/people/contacts/:id/phones/:phoneId — remove phone
router.delete(
  '/contacts/:id/phones/:phoneId',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const phoneId = req.params.phoneId as string;
      await peopleService.removePhone(id, phoneId);
      res.status(204).send();
    } catch (err) {
      next(err);
    }
  }
);

// ---------------------------------------------------------------------------
// Connections
// ---------------------------------------------------------------------------

// GET /api/people/contacts/:id/connections — list connections for a contact
router.get(
  '/contacts/:id/connections',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const connections = await peopleService.getConnections(id);
      res.json({ connections });
    } catch (err) {
      next(err);
    }
  }
);

// POST /api/people/contacts/:id/connections — create a new connection
router.post(
  '/contacts/:id/connections',
  validate(createConnectionSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const connection = await peopleService.createConnection(id, req.body, req.user!.id);
      res.status(201).json(connection);
    } catch (err) {
      next(err);
    }
  }
);

// DELETE /api/people/connections/:id — remove a connection
router.delete(
  '/connections/:id',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      await peopleService.deleteConnection(id);
      res.status(204).send();
    } catch (err) {
      next(err);
    }
  }
);

// ---------------------------------------------------------------------------
// Profile Preview (admin)
// ---------------------------------------------------------------------------

// GET /api/people/contacts/:id/profile-preview — admin preview of contact profile
router.get(
  '/contacts/:id/profile-preview',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const profile = await profileService.getProfileByContactId(id);
      if (!profile) {
        res.status(404).json({ error: { message: 'Contact not found', code: 'NOT_FOUND' } });
        return;
      }
      res.json(profile);
    } catch (err) {
      next(err);
    }
  }
);

// GET /api/people/contacts/:id/profile-photo — serve cover photo by contact ID
router.get(
  '/contacts/:id/profile-photo',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const size = (req.query.size as string) === 'thumb' ? 'thumb' : 'cover';
      const result = profileService.serveCoverPhotoByContactId(id, size as 'cover' | 'thumb');
      if (!result) {
        res.status(404).json({ error: { message: 'Photo not found', code: 'NOT_FOUND' } });
        return;
      }
      res.setHeader('Content-Type', result.contentType);
      res.setHeader('Cache-Control', 'private, max-age=300');
      result.stream.pipe(res);
    } catch (err) {
      next(err);
    }
  }
);

// ---------------------------------------------------------------------------
// Create User from Contact
// ---------------------------------------------------------------------------

// POST /api/people/contacts/:id/create-user — create user account from contact
router.post(
  '/contacts/:id/create-user',
  validate(createUserFromContactSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const result = await peopleService.createUserFromContact(id, req.body, req.user!.id);
      res.status(201).json(result);
    } catch (err) {
      next(err);
    }
  }
);

// ---------------------------------------------------------------------------
// CRM Tag Management
// ---------------------------------------------------------------------------

// GET /api/people/tags — list all registered CRM tags
router.get(
  '/tags',
  async (_req: Request, res: Response, next: NextFunction) => {
    try {
      const tags = await tagsService.listTags();
      res.json({ tags });
    } catch (err) {
      next(err);
    }
  }
);

// GET /api/people/tags/unregistered — discover tags not in registry
router.get(
  '/tags/unregistered',
  async (_req: Request, res: Response, next: NextFunction) => {
    try {
      const tags = await tagsService.getUnregisteredTags();
      res.json({ tags });
    } catch (err) {
      next(err);
    }
  }
);

// POST /api/people/tags — create a new CRM tag
router.post(
  '/tags',
  validate(createTagSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const tag = await tagsService.createTag(req.body, req.user!.id);
      res.status(201).json(tag);
    } catch (err) {
      next(err);
    }
  }
);

// POST /api/people/tags/register-all — bulk register unregistered tags
router.post(
  '/tags/register-all',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const syncToListmonk = req.body.syncToListmonk === true;
      const result = await tagsService.registerExistingTags({ syncToListmonk }, req.user!.id);
      res.json(result);
    } catch (err) {
      next(err);
    }
  }
);

// POST /api/people/tags/bulk-add — add tag to multiple contacts
router.post(
  '/tags/bulk-add',
  validate(bulkTagSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const result = await tagsService.bulkAddTag(req.body);
      res.json(result);
    } catch (err) {
      next(err);
    }
  }
);

// POST /api/people/tags/bulk-remove — remove tag from multiple contacts
router.post(
  '/tags/bulk-remove',
  validate(bulkTagSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const result = await tagsService.bulkRemoveTag(req.body);
      res.json(result);
    } catch (err) {
      next(err);
    }
  }
);

// POST /api/people/tags/sync-all — sync all tags to Listmonk
router.post(
  '/tags/sync-all',
  async (_req: Request, res: Response, next: NextFunction) => {
    try {
      const result = await tagsService.syncAllTagsToListmonk();
      res.json(result);
    } catch (err) {
      next(err);
    }
  }
);

// PUT /api/people/tags/:id — update a CRM tag
router.put(
  '/tags/:id',
  validate(updateTagSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const tag = await tagsService.updateTag(id, req.body);
      res.json(tag);
    } catch (err) {
      next(err);
    }
  }
);

// DELETE /api/people/tags/:id — delete a CRM tag
router.delete(
  '/tags/:id',
  validate(deleteTagQuerySchema, 'query'),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      await tagsService.deleteTag(id, {
        removeFromContacts: req.query.removeFromContacts === 'true',
        deleteListmonkList: req.query.deleteListmonkList === 'true',
      });
      res.status(204).send();
    } catch (err) {
      next(err);
    }
  }
);

// POST /api/people/tags/:id/sync — sync single tag to Listmonk
router.post(
  '/tags/:id/sync',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const result = await tagsService.syncTagToListmonk(id);
      res.json(result);
    } catch (err) {
      next(err);
    }
  }
);

// POST /api/people/tags/:id/recount — recalculate tag contact count
router.post(
  '/tags/:id/recount',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      await tagsService.recalculateCounts(id);
      const tag = await prisma.crmTag.findUnique({ where: { id } });
      res.json(tag);
    } catch (err) {
      next(err);
    }
  }
);

// ---------------------------------------------------------------------------
// Person detail & activity
// ---------------------------------------------------------------------------

// GET /api/people/:type/:id — get a single person with engagement
router.get(
  '/:type/:id',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const type = req.params.type as string;
      const id = req.params.id as string;
      const result = await peopleService.getPersonDetail(type, id);
      res.json(result);
    } catch (err) {
      next(err);
    }
  }
);

// GET /api/people/:type/:id/activity — activity timeline for a person
router.get(
  '/:type/:id/activity',
  validate(activityListSchema, 'query'),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const type = req.params.type as string;
      const id = req.params.id as string;
      const result = await peopleService.getActivity(type, id, req.query as any);
      res.json(result);
    } catch (err) {
      next(err);
    }
  }
);

export { router as peopleRouter };
