"""Sensor for retrieving latest GitLab CI job information."""
from __future__ import annotations

from datetime import timedelta
import logging

from gitlab import Gitlab, GitlabAuthenticationError, GitlabGetError
import voluptuous as vol

from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
from homeassistant.const import CONF_NAME, CONF_SCAN_INTERVAL, CONF_TOKEN, CONF_URL
from homeassistant.core import HomeAssistant
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.util import Throttle

_LOGGER = logging.getLogger(__name__)

ATTR_BUILD_BRANCH = "build branch"
ATTR_BUILD_COMMIT_DATE = "commit date"
ATTR_BUILD_COMMIT_ID = "commit id"
ATTR_BUILD_DURATION = "build_duration"
ATTR_BUILD_FINISHED = "build_finished"
ATTR_BUILD_ID = "build id"
ATTR_BUILD_STARTED = "build_started"
ATTR_BUILD_STATUS = "build_status"
ATTRIBUTION = "Information provided by https://gitlab.com/"

CONF_GITLAB_ID = "gitlab_id"

DEFAULT_NAME = "GitLab CI Status"
DEFAULT_URL = "https://gitlab.com"

ICON_HAPPY = "mdi:emoticon-happy"
ICON_OTHER = "mdi:git"
ICON_SAD = "mdi:emoticon-sad"

SCAN_INTERVAL = timedelta(seconds=300)

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
    {
        vol.Required(CONF_GITLAB_ID): cv.string,
        vol.Required(CONF_TOKEN): cv.string,
        vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
        vol.Optional(CONF_URL, default=DEFAULT_URL): cv.string,
    }
)


def setup_platform(
    hass: HomeAssistant,
    config: ConfigType,
    add_entities: AddEntitiesCallback,
    discovery_info: DiscoveryInfoType | None = None,
) -> None:
    """Set up the GitLab sensor platform."""
    _name = config.get(CONF_NAME, DEFAULT_NAME)
    _interval = config.get(CONF_SCAN_INTERVAL, SCAN_INTERVAL)
    _url = config.get(CONF_URL)

    _gitlab_data = GitLabData(
        priv_token=config[CONF_TOKEN],
        gitlab_id=config[CONF_GITLAB_ID],
        interval=_interval,
        url=_url,
    )

    add_entities([GitLabSensor(_gitlab_data, _name)], True)


class GitLabSensor(SensorEntity):
    """Representation of a GitLab sensor."""

    _attr_attribution = ATTRIBUTION

    def __init__(self, gitlab_data: GitLabData, name: str) -> None:
        """Initialize the GitLab sensor."""
        self._attr_available = False
        self._gitlab_data = gitlab_data
        self._attr_name = name

    @property
    def icon(self) -> str:
        """Return the icon to use in the frontend."""
        if self.native_value == "success":
            return ICON_HAPPY
        if self.native_value == "failed":
            return ICON_SAD
        return ICON_OTHER

    def update(self) -> None:
        """Collect updated data from GitLab API."""
        self._gitlab_data.update()

        self._attr_native_value = self._gitlab_data.status
        self._attr_extra_state_attributes = {
            ATTR_BUILD_STATUS: self._gitlab_data.status,
            ATTR_BUILD_STARTED: self._gitlab_data.started_at,
            ATTR_BUILD_FINISHED: self._gitlab_data.finished_at,
            ATTR_BUILD_DURATION: self._gitlab_data.duration,
            ATTR_BUILD_COMMIT_ID: self._gitlab_data.commit_id,
            ATTR_BUILD_COMMIT_DATE: self._gitlab_data.commit_date,
            ATTR_BUILD_ID: self._gitlab_data.build_id,
            ATTR_BUILD_BRANCH: self._gitlab_data.branch,
        }
        self._attr_available = self._gitlab_data.available


class GitLabData:
    """GitLab Data object."""

    def __init__(self, gitlab_id, priv_token, interval, url):
        """Fetch data from GitLab API for most recent CI job."""

        self._gitlab_id = gitlab_id
        self._gitlab = Gitlab(url, private_token=priv_token, per_page=1)
        self._gitlab.auth()
        self.update = Throttle(interval)(self._update)

        self.available = False
        self.status = None
        self.started_at = None
        self.finished_at = None
        self.duration = None
        self.commit_id = None
        self.commit_date = None
        self.build_id = None
        self.branch = None

    def _update(self) -> None:
        try:
            _projects = self._gitlab.projects.get(self._gitlab_id)
            _last_pipeline = _projects.pipelines.list(page=1)[0]
            _last_job = _last_pipeline.jobs.list(page=1)[0]
            self.status = _last_pipeline.attributes.get("status")
            self.started_at = _last_job.attributes.get("started_at")
            self.finished_at = _last_job.attributes.get("finished_at")
            self.duration = _last_job.attributes.get("duration")
            _commit = _last_job.attributes.get("commit")
            self.commit_id = _commit.get("id")
            self.commit_date = _commit.get("committed_date")
            self.build_id = _last_job.attributes.get("id")
            self.branch = _last_job.attributes.get("ref")
            self.available = True
        except GitlabAuthenticationError as erra:
            _LOGGER.error("Authentication Error: %s", erra)
            self.available = False
        except GitlabGetError as errg:
            _LOGGER.error("Project Not Found: %s", errg)
            self.available = False