from __future__ import absolute_import
from __future__ import unicode_literals

import json

import jsonschema

from .__version__ import __version__
from .exceptions import InvalidProjectFile
from .packages import Dependency
from .packages import Locker
from .packages import Package
from .repositories import Pool
from .repositories.pypi_repository import PyPiRepository
from .spdx import license_by_id
from .utils._compat import Path
from .utils.toml_file import TomlFile


class Poetry:

    VERSION = __version__

    def __init__(self,
                 file,     # type: Path
                 config,   # type: dict
                 package,  # type: Package
                 locker  # type: Locker
                 ):
        self._file = TomlFile(file)
        self._package = package
        self._config = config
        self._locker = locker

        # Configure sources
        self._pool = Pool()
        for source in self._config.get('source', []):
            self._pool.configure(source)

        # Always put PyPI last to prefere private repositories
        self._pool.add_repository(PyPiRepository())
        
    @property
    def file(self):
        return self._file

    @property
    def package(self):  # type: () -> Package
        return self._package

    @property
    def config(self):  # type: () -> dict
        return self._config

    @property
    def locker(self):  # type: () -> Locker
        return self._locker

    @property
    def pool(self):  # type: () -> Pool
        return self._pool

    @classmethod
    def create(cls, cwd):  # type: () -> Poetry
        poetry_file = Path(cwd) / 'pyproject.toml'

        if not poetry_file.exists():
            raise RuntimeError(
                'Poetry could not find a pyproject.toml file in {}'.format(cwd)
            )

        local_config = TomlFile(poetry_file.as_posix()).read(True)
        if 'tool' not in local_config or 'poetry' not in local_config['tool']:
            raise RuntimeError(
                '[tool.poetry] section not found in {}'.format(poetry_file.name)
            )
        local_config = local_config['tool']['poetry']

        # Checking validity
        cls.check(local_config)

        # Load package
        name = local_config['name']
        version = local_config['version']
        package = Package(name, version, version)

        for author in local_config['authors']:
            package.authors.append(author)

        package.description = local_config.get('description', '')
        package.homepage = local_config.get('homepage')
        package.repository_url = local_config.get('repository')
        package.license = local_config.get('license')
        package.keywords = local_config.get('keywords', [])
        package.classifiers = local_config.get('classifiers', [])

        if 'readme' in local_config:
            package.readme = Path(cwd) / local_config['readme']

        if 'platform' in local_config:
            package.platform = local_config['platform']

        if 'dependencies' in local_config:
            for name, constraint in local_config['dependencies'].items():
                if name.lower() == 'python':
                    package.python_versions = constraint
                    continue

                package.add_dependency(name, constraint)

        if 'dev-dependencies' in local_config:
            for name, constraint in local_config['dev-dependencies'].items():
                package.add_dependency(name, constraint, category='dev')

        extras = local_config.get('extras', {})
        for extra_name, requirements in extras.items():
            package.extras[extra_name] = []

            # Checking for dependency
            for req in requirements:
                req = Dependency(req, '*')

                for dep in package.requires:
                    if dep.name == req.name:
                        dep.in_extras.append(extra_name)
                        package.extras[extra_name].append(dep)

                        break

        if 'build' in local_config:
            package.build = local_config['build']

        if 'include' in local_config:
            package.include = local_config['include']

        if 'exclude' in local_config:
            package.exclude = local_config['exclude']

        locker = Locker(poetry_file.with_suffix('.lock'), local_config)

        return cls(poetry_file, local_config, package, locker)

    @classmethod
    def check(cls, config, strict=False):  # type: (dict, bool) -> bool
        """
        Checks the validity of a configuration
        """
        schema = (
            Path(__file__).parent
            / 'json' / 'schemas' / 'poetry-schema.json'
        )

        with schema.open() as f:
            schema = json.loads(f.read())

        try:
            jsonschema.validate(
                config,
                schema
            )
        except jsonschema.ValidationError as e:
            message = e.message
            if e.path:
                message = "[{}] {}".format(
                    '.'.join(e.path),
                    message
                )

            raise InvalidProjectFile(message)

        if strict:
            # If strict, check the file more thoroughly

            # Checking license
            license = config.get('license')
            if license:
                try:
                    license_by_id(license)
                except ValueError:
                    raise InvalidProjectFile('Invalid license')

        return True
