import { Router, Request, Response, NextFunction } from 'express';
import multer from 'multer';
import { validate } from '../../middleware/validate';
import { optionalAuth } from '../../middleware/auth.middleware';
import { profileViewRateLimit, profileEditRateLimit, profilePhotoRateLimit, profilePasswordRateLimit } from '../../middleware/rate-limit';
import { profileService } from './profile.service';
import { profileSelfUpdateSchema, profileActivitySchema, profilePasswordSchema } from './profile-public.schemas';

const router = Router();

// Profile tokens are crypto.randomBytes(32).toString('hex') → exactly 64 hex chars
const TOKEN_RE = /^[0-9a-f]{64}$/;

// Multer for cover photo upload — memory storage, 5MB limit
const upload = multer({
  storage: multer.memoryStorage(),
  limits: { fileSize: 5 * 1024 * 1024 },
  fileFilter: (_req, file, cb) => {
    const allowed = ['image/jpeg', 'image/png', 'image/webp'];
    if (!allowed.includes(file.mimetype)) {
      cb(new Error('Invalid file type. Allowed: JPEG, PNG, WebP'));
      return;
    }
    cb(null, true);
  },
});

// ---------------------------------------------------------------------------
// GET /api/profile/:token — Get contact profile (public)
// Checks expiration + password protection before returning profile
// ---------------------------------------------------------------------------
router.get(
  '/:token',
  profileViewRateLimit,
  optionalAuth,
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const token = req.params.token as string;
      if (!token || !TOKEN_RE.test(token)) {
        res.status(400).json({ error: { message: 'Invalid token', code: 'INVALID_TOKEN' } });
        return;
      }

      // Pre-check access (expiration + password)
      const access = await profileService.validateProfileAccess(token);

      if (access.status === 'not_found') {
        res.status(404).json({ error: { message: 'Profile not found', code: 'NOT_FOUND' } });
        return;
      }
      if (access.status === 'expired') {
        res.status(410).json({
          error: { message: 'This profile link has expired', code: 'LINK_EXPIRED' },
          expiresAt: access.expiresAt.toISOString(),
        });
        return;
      }
      if (access.status === 'password_required') {
        res.status(401).json({
          error: { message: 'Password required', code: 'PASSWORD_REQUIRED' },
          branding: access.branding,
        });
        return;
      }

      // Access OK — return full profile (pass viewer ID for isOwnProfile check)
      const profile = await profileService.getProfileByToken(token, req.user?.id);
      if (!profile) {
        res.status(404).json({ error: { message: 'Profile not found', code: 'NOT_FOUND' } });
        return;
      }

      res.json(profile);
    } catch (err) {
      next(err);
    }
  },
);

// ---------------------------------------------------------------------------
// POST /api/profile/:token/verify — Verify password for protected profile
// ---------------------------------------------------------------------------
router.post(
  '/:token/verify',
  profilePasswordRateLimit,
  validate(profilePasswordSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const token = req.params.token as string;
      if (!token || !TOKEN_RE.test(token)) {
        res.status(400).json({ error: { message: 'Invalid token', code: 'INVALID_TOKEN' } });
        return;
      }

      const result = await profileService.verifyProfilePassword(token, req.body.password);

      switch (result.status) {
        case 'not_found':
          res.status(404).json({ error: { message: 'Profile not found', code: 'NOT_FOUND' } });
          return;
        case 'expired':
          res.status(410).json({
            error: { message: 'This profile link has expired', code: 'LINK_EXPIRED' },
            expiresAt: result.expiresAt.toISOString(),
          });
          return;
        case 'invalid_password':
          res.status(401).json({ error: { message: 'Incorrect password', code: 'INVALID_PASSWORD' } });
          return;
        case 'ok':
          res.json(result.profile);
          return;
      }
    } catch (err) {
      next(err);
    }
  },
);

// ---------------------------------------------------------------------------
// PUT /api/profile/:token — Update self-editable fields
// ---------------------------------------------------------------------------
router.put(
  '/:token',
  profileEditRateLimit,
  validate(profileSelfUpdateSchema),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const token = req.params.token as string;
      if (!token || !TOKEN_RE.test(token)) {
        res.status(400).json({ error: { message: 'Invalid token', code: 'INVALID_TOKEN' } });
        return;
      }

      const updated = await profileService.updateProfileSelfService(token, req.body);
      if (!updated) {
        res.status(404).json({ error: { message: 'Profile not found', code: 'NOT_FOUND' } });
        return;
      }

      res.json({ success: true });
    } catch (err) {
      next(err);
    }
  },
);

// ---------------------------------------------------------------------------
// POST /api/profile/:token/photo — Upload cover photo
// ---------------------------------------------------------------------------
router.post(
  '/:token/photo',
  profilePhotoRateLimit,
  upload.single('photo'),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const token = req.params.token as string;
      if (!token || !TOKEN_RE.test(token)) {
        res.status(400).json({ error: { message: 'Invalid token', code: 'INVALID_TOKEN' } });
        return;
      }

      if (!req.file) {
        res.status(400).json({ error: { message: 'No file provided', code: 'NO_FILE' } });
        return;
      }

      const result = await profileService.uploadCoverPhoto(
        token,
        req.file.buffer,
        req.file.mimetype,
        req.file.originalname,
      );

      if (!result) {
        res.status(404).json({ error: { message: 'Profile not found', code: 'NOT_FOUND' } });
        return;
      }

      res.json(result);
    } catch (err) {
      if (err instanceof Error && (
        err.message.includes('Invalid file type') ||
        err.message.includes('File too large') ||
        err.message.includes('Invalid image file')
      )) {
        res.status(400).json({ error: { message: err.message, code: 'INVALID_FILE' } });
        return;
      }
      next(err);
    }
  },
);

// ---------------------------------------------------------------------------
// GET /api/profile/:token/photo — Serve cover photo
// ---------------------------------------------------------------------------
router.get(
  '/:token/photo',
  profileViewRateLimit,
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const token = req.params.token as string;
      if (!token || !TOKEN_RE.test(token)) {
        res.status(400).json({ error: { message: 'Invalid token', code: 'INVALID_TOKEN' } });
        return;
      }

      const size = req.query.size === 'thumb' ? 'thumb' : 'cover';
      const result = await profileService.serveCoverPhoto(token, 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', 'public, max-age=3600');
      result.stream.pipe(res);
    } catch (err) {
      next(err);
    }
  },
);

// ---------------------------------------------------------------------------
// GET /api/profile/:token/activity — Activity timeline (filtered for public)
// ---------------------------------------------------------------------------
router.get(
  '/:token/activity',
  profileViewRateLimit,
  validate(profileActivitySchema, 'query'),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const token = req.params.token as string;
      if (!token || !TOKEN_RE.test(token)) {
        res.status(400).json({ error: { message: 'Invalid token', code: 'INVALID_TOKEN' } });
        return;
      }

      const result = await profileService.getProfileActivity(token, req.query as any);
      if (!result) {
        res.status(404).json({ error: { message: 'Profile not found', code: 'NOT_FOUND' } });
        return;
      }

      res.json(result);
    } catch (err) {
      next(err);
    }
  },
);

export { router as profilePublicRouter };
