#!/usr/bin/env python
USAGE = """S.M.A.R.T status

More user friendly smartctl status command
"""

import fnmatch
import json
import os

from systematic.shell import Script, ScriptCommand
from systematic.stats.hardware.smart import SmartCtlClient, SmartError, ATTRIBUTE_COMMON_FIELDS


class SmartCommand(ScriptCommand):
    def __init__(self, *args, **kwargs):
        super(SmartCommand, self).__init__(*args, **kwargs)
        self.client = SmartCtlClient()

    def parse_args(self, args):
        """Parse common args

        """

        self.client.scan()

        if 'patterns' in args and args.patterns:
            self.drives = []
            for drive in self.client.drives:
                for pattern in args.patterns:
                    if fnmatch.fnmatch(drive.name, os.path.basename(pattern)):
                        self.drives.append(drive)
                        break

        else:
            self.drives = [drive for drive in self.client.drives if drive.is_supported]

        return args


class ListCommand(SmartCommand):
    def run(self, args):
        args = self.parse_args(args)

        for drive in self.drives:
            self.message(drive)


class InfoCommand(SmartCommand):
    def run(self, args):
        args = self.parse_args(args)

        if args.json:
            data = []
            for drive in self.drives:
                errors = []
                try:
                    details = drive.get_overview()
                    info = dict((info.field, info.value) for info in details)
                except SmartError as e:
                    info = {}
                    errors.append('Error getting SMART overview for {0}: {1}'.format(drive, e))

                try:
                    details = drive.get_attributes()
                    attributes = dict((attribute.description, attribute['raw_value']) for attribute in details.values())
                except SmartError as e:
                    attributes = {}
                    errors.append('Error listing attributes for {0}: {1}'.format(drive, e))
                data.append({
                    'device': '{0}'.format(drive.device),
                    'info': info,
                    'attributes': attributes,
                })
            self.message(json.dumps(data, indent=2))

        else:
            for drive in self.drives:
                self.message('{0}'.format(drive.device))

                try:
                    details = drive.get_overview()
                except SmartError as e:
                    script.message('Error getting SMART overview for {0}: {1}'.format(drive, e))
                    continue

                for info in details:
                    self.message('  {0:25} {1}'.format(info.field, info.value))
                self.message('  {0:25} {1}'.format('Drive health', drive.is_healthy and 'Healthy' or 'FAILING'))

                try:
                    attributes = drive.get_attributes()
                except SmartError as e:
                    script.message('Error listing attributes for {0}: {1}'.format(drive, e))
                    continue

                for key in ATTRIBUTE_COMMON_FIELDS:
                    try:
                        attribute = attributes[key]
                    except KeyError:
                        continue
                    script.message('  {0:25} {1}'.format(attribute.description, attribute['raw_value']))


script = Script()

c = script.add_subcommand(ListCommand('list', 'List drives with S.M.A.R.T. support'))
c.add_argument('patterns', nargs='*', help='Drives matching pattern')

c = script.add_subcommand(InfoCommand('info', 'Show basic info for drives'))
c.add_argument('-j', '--json', action='store_true', help='Show JSON output')
c.add_argument('patterns', nargs='*', help='Drives matching pattern')

args = script.parse_args()
