const { S3Client, PutObjectCommand, DeleteObjectCommand } = require('@aws-sdk/client-s3');
const CoffeeService = require('../../services/subscriptions/CoffeeService');
const { logger } = require('../../middleware/logger');

function buildPictureUrlFromKey(key) {
  const endpoint = process.env.EXOSCALE_ENDPOINT || '';
  const bucket = process.env.EXOSCALE_BUCKET || '';
  // If using S3-compatible endpoint with virtual-hosted-style, construct URL accordingly
  if (endpoint.startsWith('http')) {
    return `${endpoint.replace(/\/$/, '')}/${bucket}/${key}`;
  }
  return key; // fallback: store key only
}

function normalizePictureFiles(req) {
  const files = [];

  // upload.any() -> req.files is usually an array
  if (Array.isArray(req.files)) {
    files.push(...req.files);
  }

  // Defensive fallback for potential object-shaped middleware output
  if (req.files && !Array.isArray(req.files) && typeof req.files === 'object') {
    for (const value of Object.values(req.files)) {
      if (Array.isArray(value)) files.push(...value);
      else if (value) files.push(value);
    }
  }

  if (req.file) files.push(req.file);
  return files.filter(Boolean);
}

function toCoffeeResponse(row) {
  const pictures = Array.isArray(row?.pictures) && row.pictures.length
    ? row.pictures.map((p) => ({
        ...p,
        url: p.object_storage_id ? buildPictureUrlFromKey(p.object_storage_id) : ''
      }))
    : (row?.object_storage_id
      ? [{
          id: null,
          coffee_id: row.id,
          object_storage_id: row.object_storage_id,
          original_filename: row.original_filename || null,
          sort_order: 0,
          created_at: row.created_at || null,
          url: buildPictureUrlFromKey(row.object_storage_id)
        }]
      : []);

  const pictureUrls = pictures.map((p) => p.url).filter(Boolean);
  return {
    ...row,
    pictureUrl: pictureUrls[0] || '',
    pictureUrls,
    pictures,
  };
}

exports.list = async (req, res) => {
  const rows = await CoffeeService.list();
  const items = (rows || []).map((r) => toCoffeeResponse(r));
  res.json(items);
};

exports.create = async (req, res) => {
  let uploadedKeys = [];
  try {
    const { title, description, price } = req.body;
    const currency = req.body.currency || 'EUR';
    const is_featured = req.body.is_featured === 'true' || req.body.is_featured === true ? true : false;
    // Fixed billing defaults
    const billing_interval = 'month';
    const interval_count = 1;
    const state = req.body.state === 'false' || req.body.state === false ? false : true; // default available

    // If files uploaded, push to Exoscale and store all uploaded images
    let object_storage_id = null;
    let original_filename = null;
    let images = [];
    const incomingFiles = normalizePictureFiles(req);
    logger.info('[CoffeeController.create] incoming multipart files', {
      count: incomingFiles.length,
      fields: incomingFiles.map((f) => f?.fieldname).filter(Boolean),
    });
    if (incomingFiles.length > 10) {
      return res.status(400).json({ error: 'Maximum 10 images allowed' });
    }

    if (incomingFiles.length) {
      const allowedMime = ['image/jpeg','image/png','image/webp'];
      const maxBytes = 10 * 1024 * 1024; // 10MB

      const s3 = new S3Client({
        region: process.env.EXOSCALE_REGION,
        endpoint: process.env.EXOSCALE_ENDPOINT,
        credentials: {
          accessKeyId: process.env.EXOSCALE_ACCESS_KEY,
          secretAccessKey: process.env.EXOSCALE_SECRET_KEY,
        },
      });

      for (let i = 0; i < incomingFiles.length; i += 1) {
        const file = incomingFiles[i];
        if (!allowedMime.includes(file.mimetype)) {
          return res.status(400).json({ error: 'Invalid image type. Allowed: JPG, PNG, WebP' });
        }
        if (file.size > maxBytes) {
          return res.status(400).json({ error: 'Image exceeds 10MB limit' });
        }

        const key = `coffee/products/${Date.now()}_${i}_${file.originalname}`;
        await s3.send(new PutObjectCommand({
          Bucket: process.env.EXOSCALE_BUCKET,
          Key: key,
          Body: file.buffer,
          ContentType: file.mimetype,
          ACL: 'public-read'
        }));

        uploadedKeys.push(key);
        images.push({
          object_storage_id: key,
          original_filename: file.originalname,
          sort_order: i,
        });
      }

      if (images[0]) {
        object_storage_id = images[0].object_storage_id;
        original_filename = images[0].original_filename;
      }
      logger.info('[CoffeeController.create] uploaded pictures', { count: images.length });
    }

    const created = await CoffeeService.create({
      title,
      description,
      price: Number(price),
      currency,
      is_featured,
      billing_interval,
      interval_count,
      object_storage_id,
      original_filename,
      images,
      state,
    });
    res.status(201).json(toCoffeeResponse(created));
  } catch (e) {
    logger.error('[CoffeeController.create] error', { msg: e.message, stack: e.stack?.split('\n')[0] });
    // best-effort cleanup of uploaded object on failure
    try {
      if (uploadedKeys.length) {
        const s3 = new S3Client({
          region: process.env.EXOSCALE_REGION,
          endpoint: process.env.EXOSCALE_ENDPOINT,
          credentials: {
            accessKeyId: process.env.EXOSCALE_ACCESS_KEY,
            secretAccessKey: process.env.EXOSCALE_SECRET_KEY,
          },
        });
        for (const key of uploadedKeys) {
          await s3.send(new DeleteObjectCommand({ Bucket: process.env.EXOSCALE_BUCKET, Key: key }));
        }
      }
    } catch (cleanupErr) {
      logger.warn('[CoffeeController.create] cleanup failed', { msg: cleanupErr.message });
    }
    if (e.code === 'VALIDATION_ERROR') return res.status(400).json({ error: e.message, fields: e.errors });
    res.status(500).json({ error: 'Failed to create product' });
  }
};

exports.update = async (req, res) => {
  try {
    const id = parseInt(req.params.id, 10);
    const { title, description, price } = req.body;
    const currency = req.body.currency;
    const is_featured = req.body.is_featured === undefined ? undefined : (req.body.is_featured === 'true' || req.body.is_featured === true);
    const state = req.body.state === undefined ? undefined : (req.body.state === 'false' || req.body.state === false ? false : true);
    const removePicture = req.body.removePicture === 'true';

    let object_storage_id;
    let original_filename;
    let uploadedKey = null;
    if (req.file) {
      const allowedMime = ['image/jpeg','image/png','image/webp'];
      if (!allowedMime.includes(req.file.mimetype)) {
        return res.status(400).json({ error: 'Invalid image type. Allowed: JPG, PNG, WebP' });
      }
      const maxBytes = 10 * 1024 * 1024; // 10MB
      if (req.file.size > maxBytes) {
        return res.status(400).json({ error: 'Image exceeds 10MB limit' });
      }
      const s3 = new S3Client({
        region: process.env.EXOSCALE_REGION,
        endpoint: process.env.EXOSCALE_ENDPOINT,
        credentials: {
          accessKeyId: process.env.EXOSCALE_ACCESS_KEY,
          secretAccessKey: process.env.EXOSCALE_SECRET_KEY,
        },
      });
      const key = `coffee/products/${Date.now()}_${req.file.originalname}`;
      await s3.send(new PutObjectCommand({ Bucket: process.env.EXOSCALE_BUCKET, Key: key, Body: req.file.buffer, ContentType: req.file.mimetype, ACL: 'public-read' }));
      object_storage_id = key;
      original_filename = req.file.originalname;
      uploadedKey = key;
      logger.info('[CoffeeController.update] uploaded new picture', { id, key });
    }

    const current = await CoffeeService.get(id);
    if (!current) return res.status(404).json({ error: 'Not found' });

    // If removePicture requested and no new file uploaded, clear existing object_storage_id
    if (removePicture && !object_storage_id) {
      object_storage_id = null;
      original_filename = null;
      // Delete previous object if exists
      if (current && current.object_storage_id) {
        try {
          const s3del = new S3Client({
            region: process.env.EXOSCALE_REGION,
            endpoint: process.env.EXOSCALE_ENDPOINT,
            credentials: {
              accessKeyId: process.env.EXOSCALE_ACCESS_KEY,
              secretAccessKey: process.env.EXOSCALE_SECRET_KEY,
            },
          });
          await s3del.send(new DeleteObjectCommand({ Bucket: process.env.EXOSCALE_BUCKET, Key: current.object_storage_id }));
          logger.info('[CoffeeController.update] removed existing picture', { id });
        } catch (delErr) {
          logger.warn('[CoffeeController.update] remove existing picture failed', { id, msg: delErr.message });
        }
      }
    }

    const updated = await CoffeeService.update(id, {
      title: title ?? current.title,
      description: description ?? current.description,
      price: price !== undefined ? Number(price) : current.price,
      currency: currency !== undefined ? currency : current.currency,
      is_featured: is_featured !== undefined ? is_featured : !!current.is_featured,
      object_storage_id: object_storage_id !== undefined ? object_storage_id : current.object_storage_id,
      original_filename: original_filename !== undefined ? original_filename : current.original_filename,
      state: state !== undefined ? state : !!current.state,
    });
    res.json({
      ...updated,
      pictureUrl: updated?.object_storage_id ? buildPictureUrlFromKey(updated.object_storage_id) : ''
    });
  } catch (e) {
    logger.error('[CoffeeController.update] error', { msg: e.message });
    // best-effort cleanup of newly uploaded object on failure
    try {
      if (object_storage_id) {
        const s3 = new S3Client({
          region: process.env.EXOSCALE_REGION,
          endpoint: process.env.EXOSCALE_ENDPOINT,
          credentials: {
            accessKeyId: process.env.EXOSCALE_ACCESS_KEY,
            secretAccessKey: process.env.EXOSCALE_SECRET_KEY,
          },
        });
        await s3.send(new DeleteObjectCommand({ Bucket: process.env.EXOSCALE_BUCKET, Key: object_storage_id }));
      }
    } catch (cleanupErr) {
      logger.warn('[CoffeeController.update] cleanup failed', { msg: cleanupErr.message });
    }
    if (e.code === 'VALIDATION_ERROR') return res.status(400).json({ error: e.message, fields: e.errors });
    res.status(500).json({ error: 'Failed to update product' });
  }
};

exports.setState = async (req, res) => {
  try {
    const id = parseInt(req.params.id, 10);
    const { state } = req.body; // boolean
    const updated = await CoffeeService.setState(id, !!state);
    res.json({
      ...updated,
      pictureUrl: updated?.object_storage_id ? buildPictureUrlFromKey(updated.object_storage_id) : ''
    });
  } catch (e) {
    res.status(400).json({ error: e.message });
  }
};

exports.remove = async (req, res) => {
  try {
    const id = parseInt(req.params.id, 10);
    // fetch current for object_storage_id
    const current = await CoffeeService.get(id);
    const ok = await CoffeeService.delete(id);
    if (!ok) return res.status(404).json({ error: 'Not found' });
    // best-effort delete object from storage
    try {
      if (current && current.object_storage_id) {
        const s3 = new S3Client({
          region: process.env.EXOSCALE_REGION,
          endpoint: process.env.EXOSCALE_ENDPOINT,
          credentials: {
            accessKeyId: process.env.EXOSCALE_ACCESS_KEY,
            secretAccessKey: process.env.EXOSCALE_SECRET_KEY,
          },
        });
        await s3.send(new DeleteObjectCommand({ Bucket: process.env.EXOSCALE_BUCKET, Key: current.object_storage_id }));
      }
    } catch (cleanupErr) {
      logger.warn('[CoffeeController.remove] storage delete failed', { msg: cleanupErr.message });
    }
    res.status(204).end();
  } catch (e) {
    res.status(500).json({ error: 'Failed to delete product' });
  }
};

exports.listActive = async (req, res) => {
  try {
    const rows = await CoffeeService.listActive();
    const items = (rows || []).map((r) => toCoffeeResponse(r));
    res.json(items);
  } catch (e) {
    logger.error('[CoffeeController.listActive] error', { msg: e.message });
    res.status(500).json({ error: 'Failed to fetch active coffee products' });
  }
};

exports.getPictures = async (req, res) => {
  try {
    const id = parseInt(req.params.id, 10);
    if (!Number.isFinite(id) || id <= 0) {
      return res.status(400).json({ error: 'Invalid id' });
    }

    const row = await CoffeeService.get(id);
    if (!row) {
      return res.status(404).json({ error: 'Not found' });
    }

    const normalized = toCoffeeResponse(row);
    return res.json({
      coffeeId: id,
      pictures: normalized.pictures,
      pictureUrls: normalized.pictureUrls,
      pictureUrl: normalized.pictureUrl,
    });
  } catch (e) {
    logger.error('[CoffeeController.getPictures] error', { msg: e.message });
    return res.status(500).json({ error: 'Failed to fetch coffee pictures' });
  }
};

exports.editPictures = async (req, res) => {
  let uploadedKeys = [];
  try {
    const id = parseInt(req.params.id, 10);
    if (!Number.isFinite(id) || id <= 0) {
      return res.status(400).json({ error: 'Invalid id' });
    }

    const parseBoolean = (value, fallback = false) => {
      if (value === undefined || value === null || value === '') return fallback;
      if (typeof value === 'boolean') return value;
      const normalized = String(value).trim().toLowerCase();
      if (['1', 'true', 'yes', 'on'].includes(normalized)) return true;
      if (['0', 'false', 'no', 'off'].includes(normalized)) return false;
      return fallback;
    };

    const parseIds = (value) => {
      if (value === undefined || value === null || value === '') return [];
      if (Array.isArray(value)) return value.map((x) => Number(x)).filter((x) => Number.isFinite(x) && x > 0);

      const text = String(value).trim();
      if (!text) return [];

      try {
        const parsed = JSON.parse(text);
        if (Array.isArray(parsed)) {
          return parsed.map((x) => Number(x)).filter((x) => Number.isFinite(x) && x > 0);
        }
      } catch (_) {
        // Fall through to CSV parsing.
      }

      return text
        .split(',')
        .map((x) => Number(String(x).trim()))
        .filter((x) => Number.isFinite(x) && x > 0);
    };

    const replaceAll = parseBoolean(req.body?.replaceAll, false);
    const removePictureIds = parseIds(req.body?.removePictureIds);

    const incomingFiles = normalizePictureFiles(req);
    if (incomingFiles.length > 10) {
      return res.status(400).json({ error: 'Maximum 10 images allowed per edit request' });
    }

    const images = [];
    if (incomingFiles.length) {
      const allowedMime = ['image/jpeg','image/png','image/webp'];
      const maxBytes = 10 * 1024 * 1024; // 10MB

      const s3 = new S3Client({
        region: process.env.EXOSCALE_REGION,
        endpoint: process.env.EXOSCALE_ENDPOINT,
        credentials: {
          accessKeyId: process.env.EXOSCALE_ACCESS_KEY,
          secretAccessKey: process.env.EXOSCALE_SECRET_KEY,
        },
      });

      for (let i = 0; i < incomingFiles.length; i += 1) {
        const file = incomingFiles[i];
        if (!allowedMime.includes(file.mimetype)) {
          return res.status(400).json({ error: 'Invalid image type. Allowed: JPG, PNG, WebP' });
        }
        if (file.size > maxBytes) {
          return res.status(400).json({ error: 'Image exceeds 10MB limit' });
        }

        const key = `coffee/products/${Date.now()}_${i}_${file.originalname}`;
        await s3.send(new PutObjectCommand({
          Bucket: process.env.EXOSCALE_BUCKET,
          Key: key,
          Body: file.buffer,
          ContentType: file.mimetype,
          ACL: 'public-read'
        }));

        uploadedKeys.push(key);
        images.push({
          object_storage_id: key,
          original_filename: file.originalname,
        });
      }
    }

    const result = await CoffeeService.editPictures(id, {
      replaceAll,
      removePictureIds,
      images,
    });

    if (!result || !result.updated) {
      return res.status(404).json({ error: 'Not found' });
    }

    const deletedKeys = (result.deleted || [])
      .map((x) => x?.object_storage_id)
      .filter(Boolean);
    if (deletedKeys.length) {
      try {
        const s3 = new S3Client({
          region: process.env.EXOSCALE_REGION,
          endpoint: process.env.EXOSCALE_ENDPOINT,
          credentials: {
            accessKeyId: process.env.EXOSCALE_ACCESS_KEY,
            secretAccessKey: process.env.EXOSCALE_SECRET_KEY,
          },
        });

        for (const key of deletedKeys) {
          await s3.send(new DeleteObjectCommand({ Bucket: process.env.EXOSCALE_BUCKET, Key: key }));
        }
      } catch (cleanupErr) {
        logger.warn('[CoffeeController.editPictures] cleanup failed', { msg: cleanupErr.message });
      }
    }

    return res.json(toCoffeeResponse(result.updated));
  } catch (e) {
    logger.error('[CoffeeController.editPictures] error', { msg: e.message });

    try {
      if (uploadedKeys.length) {
        const s3 = new S3Client({
          region: process.env.EXOSCALE_REGION,
          endpoint: process.env.EXOSCALE_ENDPOINT,
          credentials: {
            accessKeyId: process.env.EXOSCALE_ACCESS_KEY,
            secretAccessKey: process.env.EXOSCALE_SECRET_KEY,
          },
        });

        for (const key of uploadedKeys) {
          await s3.send(new DeleteObjectCommand({ Bucket: process.env.EXOSCALE_BUCKET, Key: key }));
        }
      }
    } catch (cleanupErr) {
      logger.warn('[CoffeeController.editPictures] rollback cleanup failed', { msg: cleanupErr.message });
    }

    return res.status(500).json({ error: 'Failed to edit coffee pictures' });
  }
};
