using System;
using System.IO;
using AutoIngest.Core;
using ImageMagick;

namespace AutoIngest.Engine
{
    /// <summary>
    /// Converts any image format to ecommerce-optimized progressive JPG.
    /// Delegates all format detection to Magick.NET (RAW, HEIC, SVG, standard raster).
    /// </summary>
    public class ImageConverter
    {
        static ImageConverter()
        {
            MagickNET.SetTempDirectory(Path.GetTempPath());
        }

        public void ConvertToJpg(string inputPath, string outputPath,
            int quality = 92, bool autoOrient = true, bool stripExif = true,
            BrandingConfig? branding = null)
        {
            using var image = new MagickImage(inputPath);

            // No dimension limit: images are converted at their native resolution.
            // Older cameras (≤20MP) produce reasonably sized output and we don't want
            // to throw away detail by downscaling.

            // Honor the EXIF orientation tag by actually rotating/flipping the pixels. Phones and
            // some cameras set the tag without rotating, so the image appears sideways in any
            // viewer that doesn't read the tag (many browser upload previews don't). Done before
            // stripping metadata so the corrected orientation is baked in.
            if (autoOrient)
                image.AutoOrient();

            image.ColorSpace = ColorSpace.sRGB;
            image.Alpha(AlphaOption.Off);

            // Branding (visible watermarks) is burned in here — it's pixels, not metadata, so it
            // survives the Strip() below and any downstream re-upload. No-op when branding is off.
            BrandingRenderer.Apply(image, branding);

            // Strip EXIF/XMP/etc. EXIF can carry GPS coordinates — a phone photo embedded in an
            // eBay listing can leak the seller's location. Stripping after AutoOrient means the
            // orientation is already applied to pixels and the tag is no longer needed.
            if (stripExif)
                image.Strip();

            // EXIF copyright is written AFTER strip so the seller's attribution is the only
            // metadata that survives — camera model, GPS, software tags are all gone, and only
            // the Artist/Copyright/Description the user opted into remain. This realizes the
            // "strip everything then insert branding metadata" behavior.
            WriteCopyrightFields(image, branding);

            image.Format = MagickFormat.Jpeg;
            image.Quality = (uint)quality;
            image.Settings.SetDefine("jpeg:optimize-coding", "true");
            image.Settings.SetDefine("jpeg:progressive", "true");
            image.Settings.Interlace = Interlace.Line;

            image.Write(outputPath);
        }

        /// <summary>
        /// Embeds EXIF Artist/Copyright/ImageDescription from <paramref name="branding"/> when the
        /// user has enabled it. Called after Strip() so these are the only metadata that remain.
        /// Skips empty values so we never write blank tags.
        /// </summary>
        static void WriteCopyrightFields(MagickImage image, BrandingConfig? branding)
        {
            if (branding == null || !branding.Enabled || !branding.ExifCopyrightEnabled) return;

            try
            {
                // ExifProfile must exist on the image for SetAttribute("exif:...") to land at write
                // time when we've just stripped. Create an empty profile if there isn't one.
                if (image.GetExifProfile() == null)
                    image.SetProfile(new ExifProfile());

                if (!string.IsNullOrWhiteSpace(branding.ExifArtist))
                    image.SetAttribute("exif:Artist", branding.ExifArtist.Trim());
                if (!string.IsNullOrWhiteSpace(branding.ExifCopyright))
                    image.SetAttribute("exif:Copyright", branding.ExifCopyright.Trim());
                if (!string.IsNullOrWhiteSpace(branding.ExifDescription))
                    image.SetAttribute("exif:ImageDescription", branding.ExifDescription.Trim());
            }
            catch (MagickException)
            {
                // Profile/attribute write failed — non-fatal; the image still writes without the
                // copyright tags. The pixels and watermark are unaffected.
            }
        }

        public bool CanRead(string path)
        {
            // One MagickException catch covers corrupt/delegate/unknown-format errors (all derive
            // from it). No bare catch — non-Magick failures (OOM, IO) should surface, not mask as
            // "unreadable image."
            try
            {
                _ = new MagickImageInfo(path);
                return true;
            }
            catch (MagickException) { return false; }
        }
    }
}
