<?php

namespace App\Services\HubSpot;

use Carbon\Carbon;

/**
 * HubSpot Sync Metrics
 *
 * Data Transfer Object for tracking and reporting sync metrics.
 */
class HubSpotSyncMetrics
{
    /**
     * @var Carbon|null
     */
    protected $startTime = null;

    /**
     * @var Carbon|null
     */
    protected $endTime = null;

    /**
     * @var bool
     */
    protected $dryRun = false;

    /**
     * @var array List names that were created
     */
    protected $listsCreated = [];

    /**
     * @var array List names that were deleted
     */
    protected $listsDeleted = [];

    /**
     * @var array List names that were processed
     */
    protected $listsProcessed = [];

    /**
     * @var array Contacts added per list ['listTitle' => ['email1', 'email2']]
     */
    protected $contactsAdded = [];

    /**
     * @var array Contacts removed per list ['listTitle' => ['email1', 'email2']]
     */
    protected $contactsRemoved = [];

    /**
     * @var array Contacts skipped (manual, not server-synced) per list ['listTitle' => ['email1', 'email2']]
     */
    protected $contactsSkipped = [];

    /**
     * @var array Contacts kept per list ['listTitle' => count]
     */
    protected $contactsKept = [];

    /**
     * @var array Error messages with context
     */
    protected $errors = [];

    /**
     * @var array Warning messages
     */
    protected $warnings = [];

    /**
     * @var int Total committees in database
     */
    protected $totalCommittees = 0;

    /**
     * @var int Total workgroups in database
     */
    protected $totalWorkGroups = 0;

    /**
     * @var int Total users synced
     */
    protected $totalUsers = 0;

    /**
     * Start tracking the sync.
     *
     * @return self
     */
    public function start(): self
    {
        $this->startTime = Carbon::now();
        return $this;
    }

    /**
     * End tracking the sync.
     *
     * @return self
     */
    public function end(): self
    {
        $this->endTime = Carbon::now();
        return $this;
    }

    /**
     * Set dry-run mode.
     *
     * @param bool $dryRun
     * @return self
     */
    public function setDryRun(bool $dryRun): self
    {
        $this->dryRun = $dryRun;
        return $this;
    }

    /**
     * Check if this was a dry-run.
     *
     * @return bool
     */
    public function isDryRun(): bool
    {
        return $this->dryRun;
    }

    /**
     * Record a list creation.
     *
     * @param string $listTitle
     * @return self
     */
    public function recordListCreated(string $listTitle): self
    {
        $this->listsCreated[] = $listTitle;
        return $this;
    }

    /**
     * Record a list deletion.
     *
     * @param string $listTitle
     * @return self
     */
    public function recordListDeleted(string $listTitle): self
    {
        $this->listsDeleted[] = $listTitle;
        return $this;
    }

    /**
     * Record a list being processed.
     *
     * @param string $listTitle
     * @return self
     */
    public function recordListProcessed(string $listTitle): self
    {
        $this->listsProcessed[] = $listTitle;
        return $this;
    }

    /**
     * Record contacts added to a list.
     *
     * @param string $listTitle
     * @param array $emails
     * @return self
     */
    public function recordContactsAdded(string $listTitle, array $emails): self
    {
        if (!isset($this->contactsAdded[$listTitle])) {
            $this->contactsAdded[$listTitle] = [];
        }
        $this->contactsAdded[$listTitle] = array_merge($this->contactsAdded[$listTitle], $emails);
        return $this;
    }

    /**
     * Record contacts removed from a list.
     *
     * @param string $listTitle
     * @param array $emails
     * @return self
     */
    public function recordContactsRemoved(string $listTitle, array $emails): self
    {
        if (!isset($this->contactsRemoved[$listTitle])) {
            $this->contactsRemoved[$listTitle] = [];
        }
        $this->contactsRemoved[$listTitle] = array_merge($this->contactsRemoved[$listTitle], $emails);
        return $this;
    }

    /**
     * Record contacts skipped (manual, not server-synced).
     *
     * @param string $listTitle
     * @param array $emails
     * @return self
     */
    public function recordContactsSkipped(string $listTitle, array $emails): self
    {
        if (!isset($this->contactsSkipped[$listTitle])) {
            $this->contactsSkipped[$listTitle] = [];
        }
        $this->contactsSkipped[$listTitle] = array_merge($this->contactsSkipped[$listTitle], $emails);
        return $this;
    }

    /**
     * Record contacts kept in a list.
     *
     * @param string $listTitle
     * @param int $count
     * @return self
     */
    public function recordContactsKept(string $listTitle, int $count): self
    {
        $this->contactsKept[$listTitle] = $count;
        return $this;
    }

    /**
     * Record an error.
     *
     * @param string $message
     * @param string|null $context
     * @return self
     */
    public function recordError(string $message, ?string $context = null): self
    {
        $this->errors[] = [
            'message' => $message,
            'context' => $context,
            'timestamp' => Carbon::now()->toIso8601String(),
        ];
        return $this;
    }

    /**
     * Record a warning.
     *
     * @param string $message
     * @return self
     */
    public function recordWarning(string $message): self
    {
        $this->warnings[] = $message;
        return $this;
    }

    /**
     * Set total committees count.
     *
     * @param int $count
     * @return self
     */
    public function setTotalCommittees(int $count): self
    {
        $this->totalCommittees = $count;
        return $this;
    }

    /**
     * Set total workgroups count.
     *
     * @param int $count
     * @return self
     */
    public function setTotalWorkGroups(int $count): self
    {
        $this->totalWorkGroups = $count;
        return $this;
    }

    /**
     * Set total users count.
     *
     * @param int $count
     * @return self
     */
    public function setTotalUsers(int $count): self
    {
        $this->totalUsers = $count;
        return $this;
    }

    /**
     * Get duration in seconds.
     *
     * @return float|null
     */
    public function getDurationSeconds(): ?float
    {
        if ($this->startTime === null || $this->endTime === null) {
            return null;
        }
        return $this->startTime->floatDiffInSeconds($this->endTime);
    }

    /**
     * Get human-readable duration.
     *
     * @return string
     */
    public function getDurationHuman(): string
    {
        $seconds = $this->getDurationSeconds();
        if ($seconds === null) {
            return 'N/A';
        }

        if ($seconds < 60) {
            return round($seconds, 2) . ' seconds';
        }

        $minutes = floor($seconds / 60);
        $remainingSeconds = round($seconds % 60);

        return "{$minutes} min {$remainingSeconds} sec";
    }

    /**
     * Check if there are any errors.
     *
     * @return bool
     */
    public function hasErrors(): bool
    {
        return !empty($this->errors);
    }

    /**
     * Get total contacts added count.
     *
     * @return int
     */
    public function getTotalContactsAdded(): int
    {
        $total = 0;
        foreach ($this->contactsAdded as $emails) {
            $total += count($emails);
        }
        return $total;
    }

    /**
     * Get total contacts removed count.
     *
     * @return int
     */
    public function getTotalContactsRemoved(): int
    {
        $total = 0;
        foreach ($this->contactsRemoved as $emails) {
            $total += count($emails);
        }
        return $total;
    }

    /**
     * Get total contacts skipped count.
     *
     * @return int
     */
    public function getTotalContactsSkipped(): int
    {
        $total = 0;
        foreach ($this->contactsSkipped as $emails) {
            $total += count($emails);
        }
        return $total;
    }

    /**
     * Get total contacts kept count.
     *
     * @return int
     */
    public function getTotalContactsKept(): int
    {
        return array_sum($this->contactsKept);
    }

    /**
     * Convert metrics to array.
     *
     * @return array
     */
    public function toArray(): array
    {
        return [
            'dryRun' => $this->dryRun,
            'startTime' => $this->startTime?->toIso8601String(),
            'endTime' => $this->endTime?->toIso8601String(),
            'durationSeconds' => $this->getDurationSeconds(),
            'durationHuman' => $this->getDurationHuman(),
            'database' => [
                'totalCommittees' => $this->totalCommittees,
                'totalWorkGroups' => $this->totalWorkGroups,
                'totalUsers' => $this->totalUsers,
            ],
            'lists' => [
                'created' => $this->listsCreated,
                'deleted' => $this->listsDeleted,
                'processed' => $this->listsProcessed,
                'createdCount' => count($this->listsCreated),
                'deletedCount' => count($this->listsDeleted),
                'processedCount' => count($this->listsProcessed),
            ],
            'contacts' => [
                'added' => $this->contactsAdded,
                'removed' => $this->contactsRemoved,
                'kept' => $this->contactsKept,
                'totalAdded' => $this->getTotalContactsAdded(),
                'totalRemoved' => $this->getTotalContactsRemoved(),
                'totalKept' => $this->getTotalContactsKept(),
            ],
            'errors' => $this->errors,
            'warnings' => $this->warnings,
            'hasErrors' => $this->hasErrors(),
        ];
    }

    /**
     * Get a summary string for console output.
     *
     * @return string
     */
    public function toSummary(): string
    {
        $mode = $this->dryRun ? '[DRY-RUN] ' : '';
        $status = $this->hasErrors() ? '❌ WITH ERRORS' : '✅ SUCCESS';

        $lines = [
            "",
            "═══════════════════════════════════════════════════════════════",
            "{$mode}HubSpot Sync Report - {$status}",
            "═══════════════════════════════════════════════════════════════",
            "",
            "⏱️  Duration: {$this->getDurationHuman()}",
            "",
            "📊 Database Summary:",
            "   • Committees: {$this->totalCommittees}",
            "   • WorkGroups: {$this->totalWorkGroups}",
            "   • Active Users: {$this->totalUsers}",
            "",
            "📋 Lists:",
            "   • Created: " . count($this->listsCreated),
            "   • Deleted: " . count($this->listsDeleted),
            "   • Processed: " . count($this->listsProcessed),
            "",
            "👥 Contacts:",
            "   • Added: {$this->getTotalContactsAdded()}",
            "   • Removed: {$this->getTotalContactsRemoved()}",
            "   • Kept: {$this->getTotalContactsKept()}",
            "   • Skipped (manual): {$this->getTotalContactsSkipped()}",
        ];

        if (!empty($this->listsCreated)) {
            $lines[] = "";
            $lines[] = "📝 Lists Created:";
            foreach ($this->listsCreated as $title) {
                $lines[] = "   • {$title}";
            }
        }

        if (!empty($this->listsDeleted)) {
            $lines[] = "";
            $lines[] = "🗑️  Lists Deleted:";
            foreach ($this->listsDeleted as $title) {
                $lines[] = "   • {$title}";
            }
        }

        if (!empty($this->errors)) {
            $lines[] = "";
            $lines[] = "❌ Errors:";
            foreach ($this->errors as $error) {
                $context = $error['context'] ? " ({$error['context']})" : "";
                $lines[] = "   • {$error['message']}{$context}";
            }
        }

        if (!empty($this->warnings)) {
            $lines[] = "";
            $lines[] = "⚠️  Warnings:";
            foreach ($this->warnings as $warning) {
                $lines[] = "   • {$warning}";
            }
        }

        $lines[] = "";
        $lines[] = "═══════════════════════════════════════════════════════════════";
        $lines[] = "";

        return implode("\n", $lines);
    }

    /**
     * Get lists created.
     *
     * @return array
     */
    public function getListsCreated(): array
    {
        return $this->listsCreated;
    }

    /**
     * Get lists deleted.
     *
     * @return array
     */
    public function getListsDeleted(): array
    {
        return $this->listsDeleted;
    }

    /**
     * Get lists processed.
     *
     * @return array
     */
    public function getListsProcessed(): array
    {
        return $this->listsProcessed;
    }

    /**
     * Get contacts added.
     *
     * @return array
     */
    public function getContactsAdded(): array
    {
        return $this->contactsAdded;
    }

    /**
     * Get contacts removed.
     *
     * @return array
     */
    public function getContactsRemoved(): array
    {
        return $this->contactsRemoved;
    }

    /**
     * Get errors.
     *
     * @return array
     */
    public function getErrors(): array
    {
        return $this->errors;
    }

    /**
     * Get warnings.
     *
     * @return array
     */
    public function getWarnings(): array
    {
        return $this->warnings;
    }

    /**
     * Get start time.
     *
     * @return Carbon|null
     */
    public function getStartTime(): ?Carbon
    {
        return $this->startTime;
    }

    /**
     * Get end time.
     *
     * @return Carbon|null
     */
    public function getEndTime(): ?Carbon
    {
        return $this->endTime;
    }
}
