<?php

declare(strict_types=1);

namespace Intervention\Image\Drivers\Imagick;

use Imagick;
use ImagickException;
use ImagickPixel;
use ImagickPixelException;
use Intervention\Image\Colors\Cmyk\Channels\Cyan;
use Intervention\Image\Colors\Cmyk\Channels\Key;
use Intervention\Image\Colors\Cmyk\Channels\Magenta;
use Intervention\Image\Colors\Cmyk\Channels\Yellow;
use Intervention\Image\Colors\Cmyk\Colorspace as Cmyk;
use Intervention\Image\Colors\Hsl\Colorspace as Hsl;
use Intervention\Image\Colors\Hsv\Colorspace as Hsv;
use Intervention\Image\Colors\Oklab\Colorspace as Oklab;
use Intervention\Image\Colors\Oklch\Colorspace as Oklch;
use Intervention\Image\Colors\Rgb\Channels\Alpha;
use Intervention\Image\Colors\Rgb\Channels\Blue;
use Intervention\Image\Colors\Rgb\Channels\Green;
use Intervention\Image\Colors\Rgb\Channels\Red;
use Intervention\Image\Colors\Rgb\Colorspace as Rgb;
use Intervention\Image\Exceptions\DriverException;
use Intervention\Image\Exceptions\InvalidArgumentException;
use Intervention\Image\Exceptions\NotSupportedException;
use Intervention\Image\Interfaces\ColorInterface;
use Intervention\Image\Interfaces\ColorProcessorInterface;
use Intervention\Image\Interfaces\ColorspaceInterface;

class ColorProcessor implements ColorProcessorInterface
{
    /**
     * Create new instance.
     */
    public function __construct(protected ColorspaceInterface $colorspace)
    {
        //
    }

    /**
     * {@inheritdoc}
     *
     * @see ColorProcessorInterface::colorspace()
     */
    public function colorspace(): ColorspaceInterface
    {
        return $this->colorspace;
    }

    /**
     * @throws DriverException
     */
    public function export(ColorInterface $color): ImagickPixel
    {
        $color = $this->colorspace->importColor($color);

        if ($this->colorspace instanceof Cmyk) {
            try {
                $pixel = new ImagickPixel();
                $pixel->setColorValue(Imagick::COLOR_CYAN, $color->channel(Cyan::class)->normalized());
                $pixel->setColorValue(Imagick::COLOR_MAGENTA, $color->channel(Magenta::class)->normalized());
                $pixel->setColorValue(Imagick::COLOR_YELLOW, $color->channel(Yellow::class)->normalized());
                $pixel->setColorValue(Imagick::COLOR_BLACK, $color->channel(Key::class)->normalized());
            } catch (ImagickException | ImagickPixelException $e) {
                throw new DriverException('Failed to create CMYK color', previous: $e);
            }

            return $pixel;
        }

        $color = $color->toColorspace(Rgb::class);

        try {
            return new ImagickPixel(
                sprintf(
                    "srgba(%s, %s, %s, %s)",
                    $color->channel(Red::class)->value(),
                    $color->channel(Green::class)->value(),
                    $color->channel(Blue::class)->value(),
                    $color->channel(Alpha::class)->toString(),
                )
            );
        } catch (ImagickException | ImagickPixelException $e) {
            throw new DriverException('Failed to create color', previous: $e);
        }
    }

    /**
     * @throws InvalidArgumentException
     * @throws DriverException
     * @throws NotSupportedException
     */
    public function import(mixed $color): ColorInterface
    {
        if (!$color instanceof ImagickPixel) {
            throw new InvalidArgumentException(
                'Imagick driver can only process colors from instances of ' . ImagickPixel::class,
            );
        }

        try {
            return match ($this->colorspace::class) {
                Cmyk::class => $this->colorspace->colorFromNormalized([
                    $color->getColorValue(Imagick::COLOR_CYAN),
                    $color->getColorValue(Imagick::COLOR_MAGENTA),
                    $color->getColorValue(Imagick::COLOR_YELLOW),
                    $color->getColorValue(Imagick::COLOR_BLACK),
                ]),
                Rgb::class => $this->colorspace->colorFromNormalized([
                    $color->getColorValue(Imagick::COLOR_RED),
                    $color->getColorValue(Imagick::COLOR_GREEN),
                    $color->getColorValue(Imagick::COLOR_BLUE),
                    $color->getColorValue(Imagick::COLOR_ALPHA),
                ]),
                Hsl::class => Rgb::class::colorFromNormalized([
                    $color->getColorValue(Imagick::COLOR_RED),
                    $color->getColorValue(Imagick::COLOR_GREEN),
                    $color->getColorValue(Imagick::COLOR_BLUE),
                    $color->getColorValue(Imagick::COLOR_ALPHA),
                ])->toColorspace(Hsl::class),
                Hsv::class => Rgb::colorFromNormalized([
                    $color->getColorValue(Imagick::COLOR_RED),
                    $color->getColorValue(Imagick::COLOR_GREEN),
                    $color->getColorValue(Imagick::COLOR_BLUE),
                    $color->getColorValue(Imagick::COLOR_ALPHA),
                ])->toColorspace(Hsv::class),
                Oklab::class => Rgb::colorFromNormalized([
                    $color->getColorValue(Imagick::COLOR_RED),
                    $color->getColorValue(Imagick::COLOR_GREEN),
                    $color->getColorValue(Imagick::COLOR_BLUE),
                    $color->getColorValue(Imagick::COLOR_ALPHA),
                ])->toColorspace(Oklab::class),
                Oklch::class => Rgb::colorFromNormalized([
                    $color->getColorValue(Imagick::COLOR_RED),
                    $color->getColorValue(Imagick::COLOR_GREEN),
                    $color->getColorValue(Imagick::COLOR_BLUE),
                    $color->getColorValue(Imagick::COLOR_ALPHA),
                ])->toColorspace(Oklch::class),
                default => throw new NotSupportedException(
                    'Colorspace ' . $this->colorspace::class . ' is not supported by driver'
                )
            };
        } catch (ImagickPixelException $e) {
            throw new DriverException(
                'Failed to import color from ' . ImagickPixel::class,
                previous: $e,
            );
        }
    }
}
