import argparse
import fnmatch
import json
import os
import sh
import subprocess


class Command(object):
    def __init__(self, cmd, *args):
        self._args = [cmd] + list(args)

    def bake(self, arg, *args):
        self._args += [arg] + list(args)
        
def create_command(cmd, *args):
    _args = [cmd] + list(args)

    def runner(*args):
        return subprocess.check_output(_args + list(args))

    return runner

arc_diff = sh.Command._create('arc').bake("diff",
                                          allow_untracked=True,
                                          browse=True)
git_diff = create_command('git',
                          'diff-tree',
                          '--no-commit-id',
                          '--name-only',
                          '-r')
git_merge_base = create_command('git', 'merge-base')


def list_changed_files(start, end=None):
    if end:
        commit_range = git_merge_base(start, end).split()[0]
        commit_range += "..%s" % (end,)
    else:
        commit_range = start
    return git_diff(commit_range).split()


def match_owners(owners_filepath, filename):
    if os.path.exists(owners_filepath):
        with open(owners_filepath, 'r') as f:
            raw_data = json.load(f)
            # handle simple file format
            if type(raw_data) == dict:
                return {
                    "reviewers": set(raw_data["reviewers"]),
                    "subscribers": set(raw_data["subscribers"])
                }
            else:
                assert type(raw_data) == list
                owners = {
                    "reviewers": set(),
                    "subscribers": set()
                }
                for data in raw_data:
                    patterns = data["includes"]
                    if any(fnmatch.fnmatch(filename, pat)
                           for pat in patterns):
                        cur_owners = {
                            "reviewers": set(data["reviewers"]),
                            "subscribers": set(data["subscribers"])
                        }
                        owners = merge_owners(owners, cur_owners)
                return owners
    else:
        return None


def merge_owners(owners1, owners2):
    return {
        "reviewers": owners1["reviewers"].union(owners2["reviewers"]),
        "subscribers": owners1["subscribers"].union(owners2["subscribers"])
    }


def get_owners(filename):
    cur_path = filename
    owners = {
        "reviewers": set(),
        "subscribers": set()
    }
    while cur_path:
        cur_dir = os.path.dirname(cur_path)
        owners_filepath = os.path.join(cur_dir, "OWNERS.json")
        cur_owners = match_owners(owners_filepath, filename)
        if cur_owners:
            owners = merge_owners(owners, cur_owners)
        # continue one level up
        cur_path = cur_dir
    return owners


def get_commit_owners(start, end=None):
    owners = {
        "reviewers": set(),
        "subscribers": set()
    }
    for f in list_changed_files(start, end):
        cur_owners = get_owners(f)
        if cur_owners:
            owners = merge_owners(owners, cur_owners)
    return owners


def create_review(start, end=None):
    owners = get_commit_owners(start, end)
    _arc_diff = arc_diff
    # take this off once done testing
    # only=True)
    if end != "HEAD":
        _arc_diff = _arc_diff.bake(head=end)
    reviewers = ','.join(owners["reviewers"])
    subs = ','.join(owners["subscribers"])
    if subs:
        _arc_diff = _arc_diff.bake(cc=subs)
    subprocess.call(str(_arc_diff.bake(start, reviewers=reviewers)),
                    shell=True)

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument("-s", "--single",
                        help="Single-commit mode. Base now means the only \
                        commit to be reviewed",
                        action="store_true")
    parser.add_argument("base",
                        help="The last commit/branch before the work to be \
                        reviewed")
    parser.add_argument("--to",
                        help="The last commit in the work to be reviewed")
    args = parser.parse_args()
    if args.single:
        last_commit = None
    elif args.to:
        last_commit = args.to
    else:
        last_commit = "HEAD"
    create_review(args.base,
                  last_commit)

@@ -1,12 +1,35 @@
+#!/usr/bin/env python%0A%0A
 import argpa

Add shebang