#!/usr/bin/env python
# encoding: utf-8
#
# Copyright 2019 Disney Enterprises, Inc. All rights reserved
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:

# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.

# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.

# * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
# Studios" or the names of its contributors may NOT be used to
# endorse or promote products derived from this software without
# specific prior written permission from Walt Disney Pictures.

# Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
# IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

'''A tool to clean abandonded products from a reposado repo'''

from __future__ import absolute_import
from __future__ import print_function

import os
import shutil
import sys

from reposadolib import reposadocommon


# Python 2 and 3 wrapper for raw_input/input
try:
    # Python 2
    get_input = raw_input # pylint: disable=raw_input-builtin, invalid-name
except NameError:
    # Python 3
    get_input = input # pylint: disable=input-builtin, invalid-name


def find_all_product_dirs_on_disk():
    '''Walks downloads dir and returns a set of all directories that appear to
    contain a product'''
    root_dir = reposadocommon.pref('UpdatesRootDir')
    downloads_dir = os.path.join(root_dir, "content/downloads")
    product_dirs = set()
    for (path, _, files) in os.walk(downloads_dir):
        for a_file in files:
            if a_file.endswith((".smd", ".pkm", ".pkg", ".mpkg", ".tar")):
                product_dirs.add(path)
                break
    return product_dirs


def get_product_location(product):
    '''Returns local path to replicated product.'''
    if not 'CatalogEntry' in product:
        # something is wrong with the product entry
        return None
    catalog_entry = product['CatalogEntry']
    product_url = None
    if 'ServerMetadataURL' in catalog_entry:
        product_url = catalog_entry['ServerMetadataURL']
    else:
        try:
            # get the URL for the first package in the Packages array
            product_url = catalog_entry['Packages'][0]['URL']
        except (KeyError, IndexError):
            return None
    filepath = reposadocommon.getLocalPathNameFromURL(product_url)
    # return the directory this pkg is in
    return os.path.dirname(filepath)


def all_product_dirs_from_productinfo(): # pylint: disable=invalid-name
    '''Returns a set of all the product directories for products in our
    ProductInformation.plist'''
    product_dirs = set()
    products = reposadocommon.getProductInfo()
    for product in products:
        product_location = get_product_location(products[product])
        if product_location:
            product_dirs.add(product_location)
    return product_dirs


def main():
    '''Here's the main thing we do!'''
    if not reposadocommon.pref('LocalCatalogURLBase'):
        print("We're not replicating products, so nothing to clean up!")
        return

    print("Finding all product directories stored on disk...")
    disk_dirs = find_all_product_dirs_on_disk()
    print("    Found %s products" % len(disk_dirs))

    print("Finding all product directories in our internal database...")
    info_dirs = all_product_dirs_from_productinfo()
    print("    Found %s products" % len(info_dirs))

    missing_disk_dirs = info_dirs - disk_dirs
    if missing_disk_dirs:
        print("\nFound %s products that might not be on-disk:"
              % len(missing_disk_dirs))
        print("\n".join(sorted(missing_disk_dirs)))

    orphaned_dirs = disk_dirs - info_dirs
    if orphaned_dirs:
        print("\nFound %s abandoned/orphaned product directories:"
              % len(orphaned_dirs))
        print("\n".join(sorted(orphaned_dirs)))
        print()
        answer = get_input(
            'Remove abandoned product directories '
            '(WARNING: this cannot be undone)? [y/n]: ')
        if answer.lower().startswith('y'):
            for directory in orphaned_dirs:
                print ("Removing %s..." % directory)
                try:
                    shutil.rmtree(directory)
                except (OSError, IOError) as err:
                    print('Error: %s' % err, file=sys.stderr)
    else:
        print("Can't find anything to clean up!")


if __name__ == '__main__':
    main()
