from __future__ import unicode_literals

import os
import pipes
from subprocess import Popen, PIPE

from django.conf import settings
from django.contrib.staticfiles.finders import find
from django.utils.encoding import smart_text

from assetfiles.filters import BaseFilter, ExtensionMixin
import assetfiles.settings


class SassFilter(ExtensionMixin, BaseFilter):
    """
    Filters Sass files into CSS.

    Attributes:
        functions_path: A path to the Sass extension functions for Django
            integration.
    """
    input_exts = ('sass', 'scss')
    output_ext = 'css'
    functions_path = os.path.abspath(
        os.path.join(os.path.dirname(__file__), '../scripts/sass_functions.rb'))

    def __init__(self, options=None, *args, **kwargs):
        super(SassFilter, self).__init__(*args, **kwargs)
        if options is None:
            options = {}
        if 'compass' not in options:
            options['compass'] = self._detect_compass()
        self.options = options

    def filter(self, input):
        command = 'sass {0} {1}'.format(
            ' '.join(self._build_args()),
            pipes.quote(input))

        env = dict(os.environ)
        env.update({'DJANGO_STATIC_URL': settings.STATIC_URL})

        process = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, env=env)
        stdout, stderr = process.communicate()

        if process.returncode:
            raise SassFilterError(stderr)
        else:
            return stdout

    def is_filterable(self, output_path):
        """
        Skips files prefixed with a '_'. These are Sass dependencies.
        """
        _, file_name = os.path.split(output_path)
        return not file_name.startswith('_')

    def _build_args(self):
        """
        Returns a list of arguments for the Sass command.
        """
        args = []
        if self.options['compass']:
            args.append('--compass')
        if self.functions_path is not None:
            args.append(
                '--require {0}'.format(pipes.quote(self.functions_path)))
        if sass_load_paths:
            for path in sass_load_paths:
                args.append('--load-path {0}'.format(pipes.quote(path)))
        return args

    def _detect_compass(self):
        """
        Returns true if Compass integration is available.
        """
        return os.system('which compass > /dev/null') is 0


def get_static_sass_dirs(dirs=None):
    """
    Returns the directories with Sass files within the static directories.

    Args:
        dirs: A list or tuple of directory names that contain Sass files.
            Can be configured with the ASSETFILES_SASS_DIRS setting, which by
            default is `('css',)`

    Returns:
        A list of directory paths containing Sass files.
    """
    if not dirs:
        dirs = assetfiles.settings.SASS_DIRS

    load_paths = []
    for dir in dirs:
        load_paths += find(dir, all=True) or []
    return load_paths


"""
Directories that will be added to the Sass load path.

By default, these are 'css' directories within the static directories.
"""
sass_load_paths = get_static_sass_dirs()


class SassFilterError(Exception):
    """
    SassError parses syntax errors generated by Sass and formats them
    for Django.
    """
    def __init__(self, stacktrace):
        stacktrace = stacktrace.strip().split(b'\n')[:-1]
        message = b'\n'.join(stacktrace)
        super(SassFilterError, self).__init__(message)
