<?php

namespace App\Model;

use Ecomnova\ProductPageAppleyardDesktop;
use Ecomnova\ProductPageInterface;
use Facebook\WebDriver\Exception\NoSuchElementException;
use PHPUnit\Framework\Assert;
use PHPUnit\Framework\ExpectationFailedException;
use Psr\Log\LoggerInterface;

class Product
{
    private BasePrice $basePrice;
    private Url $url;

    protected CsvParser $csvParser;

    /**
     * @var ProductPageInterface
     */
    private $tester = null;

    private $errors = [];
    private LoggerInterface $logger;
    private Mailer $mailer;

    private int $startTimestamp;
    private int $endTimestamp;

    public function __construct(
        BasePrice $basePrice,
        Url $url,
        CsvParser $csvParser,
        LoggerInterface $logger,
        Mailer $mailer,
    )
    {
        $this->basePrice = $basePrice;
        $this->url = $url;
        $this->csvParser = $csvParser;
        $this->logger = $logger;
        $this->mailer = $mailer;
    }

    public function testBasePrice(): void
    {
        if ($this->tester == null) {
            $this->tester = ProductPageAppleyardDesktop::getTesterInstance(ProductPageInterface::class);
            $this->tester->init();
        }
        try {
            $this->tester->openProductPage($this->url->getUrl());
            $this->logger->debug("Opened product page for {$this->url->getUrl()}");
            $this->logger->debug("Getting base price for {$this->url->getUrl()}");
            $realBasePrice = $this->getBasePrice()->getRawPrice($this->tester->getBasePrice());
            $this->logger->debug("Got base price for {$this->url->getUrl()}");
        }
        catch (\Exception $e) {
            $this->errors[] = "There was an error while testing " . $this->url->getUrl() . " The error was " . $e->getMessage();
            $this->logger->error($e->getMessage());
            return;
        }
        try {
            Assert::assertEquals($this->basePrice->getBasePrice(), $realBasePrice, "Base price does not match for {$this->url->getUrl()}. Expected {$this->basePrice->getBasePrice()} but got {$realBasePrice}");
            $this->logger->debug("Base price matches for {$this->url->getUrl()}");
        }
        catch (ExpectationFailedException $e) {
            $this->errors[] = $e->getMessage();
            $this->logger->error($e->getMessage());
        }
    }

    public function getUrl(): Url
    {
        return $this->url;
    }

    public function setUrl(Url $url): void
    {
        $this->url = $url;
    }

    public function getBasePrice(): BasePrice
    {
        return $this->basePrice;
    }

    public function setBasePrice(BasePrice $basePrice): void
    {
        $this->basePrice = $basePrice;
    }

    public function getCsvParser(): CsvParser
    {
        return $this->csvParser;
    }

    public function runTests()
    {
        $csvData = $this->getCsvParser()->getRows();
        $this->startTimestamp = time();
        foreach ($csvData['rows'] as $row) {
            $this->getUrl()->setUrl($row[0]);
            $this->getBasePrice()->setBasePrice($row[1]);
            try {
                $this->testBasePrice();
            }catch (\Exception $e) {
                $this->errors[] = "There was an error processing $row[0] " . $e->getMessage();
                $this->logger->error($e->getMessage());
            }
        }
        $this->endTimestamp = time();
        $this->mailer->sendEmail($this->getErrors(), $this->getExecutionHumanReadable($this->getExecutionTimeInMinutes()));
    }

    public function getErrors($json = false): array|string
    {
        if ($json) {
            return json_encode($this->errors);
        }
        return $this->errors;
    }

    private function getExecutionTimeInMinutes()
    {
        return round(($this->endTimestamp - $this->startTimestamp) / 60, 2);
    }

    private function getExecutionHumanReadable(float $getExecutionTimeInMinutes)
    {
        return $getExecutionTimeInMinutes . " minutes";
    }

}