# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import json
import mock
import nose
import os
import sys
import unittest

# Allow us to run even if not at the `tools` directory.
root_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
sys.path.insert(0, root_dir)

import deploy_pbz_to_pebblefw


class TestDeployPbzToPebbleFw(unittest.TestCase):
    DUMMY_MANIFEST_CONTENT = {
        'versionTag': 'v3.1',
        'timestamp': 1377981535,
        'hwrev': 'bb2',
        'type': 'normal'
    }

    @mock.patch('boto.connect_s3')
    @mock.patch('boto.s3')
    @mock.patch('deploy_pbz_to_pebblefw.open')
    @mock.patch('deploy_pbz_to_pebblefw._sha256_file')
    @mock.patch('requests.get')
    @mock.patch('libpebble2.util.bundle.PebbleBundle')
    def test_no_latest_exists_dry_run(self, mock_pebble_bundle, mock_requests_get, mock_sha256_file,
                                      mock_open, mock_boto_s3, mock_boto_connect_s3):

        # Set up our mocks
        mock_pebble_bundle_instance = mock_pebble_bundle.return_value
        mock_pebble_bundle_instance.get_manifest.return_value = {
            'firmware': self.DUMMY_MANIFEST_CONTENT
        }

        dummy_sha = '620cfcadc8d28240048ffa01eb6984b06774a584349f178564e1548ecc813903'
        mock_sha256_file.return_value = dummy_sha

        mock_requests_get.return_value.status_code = 403

        dummy_notes = 'Dummy notes'
        mock.mock_open(mock_open, read_data=dummy_notes)

        # Run the code under test
        dummy_pbz_path = 'dummy_pbz.pbz'
        dummy_notes_path = 'dummy_notes.txt'
        deploy_pbz_to_pebblefw.deploy_bundle(dummy_pbz_path, 'pebblefw-staging', 'porksmoothie',
                                             dummy_notes_path, dry_run=True)

        # Check out mocks to make sure everything worked
        mock_sha256_file.assert_called_with(dummy_pbz_path)

        mock_open.assert_called_with(dummy_notes_path, 'r')

        mock_requests_get.assert_called_with(
                'https://pebblefw-staging.s3.amazonaws.com/pebble/bb2/porksmoothie/latest.json')

        # We're using dry_run=True, we better not talk to s3
        assert not mock_boto_connect_s3.called

    @mock.patch('boto.connect_s3')
    @mock.patch('boto.s3')
    @mock.patch('deploy_pbz_to_pebblefw.open')
    @mock.patch('deploy_pbz_to_pebblefw._sha256_file')
    @mock.patch('requests.get')
    @mock.patch('libpebble2.util.bundle.PebbleBundle')
    def test_no_latest_exists_no_dry_run(self, mock_pebble_bundle, mock_requests_get,
                                         mock_sha256_file, mock_open, mock_boto_s3,
                                         mock_boto_connect_s3):

        # Set up our mocks
        mock_pebble_bundle_instance = mock_pebble_bundle.return_value
        mock_pebble_bundle_instance.get_manifest.return_value = {
            'firmware': self.DUMMY_MANIFEST_CONTENT
        }

        dummy_sha = '620cfcadc8d28240048ffa01eb6984b06774a584349f178564e1548ecc813903'
        mock_sha256_file.return_value = dummy_sha

        mock_requests_get.return_value.status_code = 403

        dummy_notes = 'Dummy notes'
        mock.mock_open(mock_open, read_data=dummy_notes)

        mock_latest_key = mock.MagicMock()

        def boto_key_func(boto_bucket, key):
            if key == 'pebble/bb2/porksmoothie/latest.json':
                # Only validate us uploading to latest.json and return unamed mocks for the other
                # paths.
                return mock_latest_key

            return mock.DEFAULT

        mock_boto_s3.key.Key.side_effect = boto_key_func

        # Run the code under test
        dummy_pbz_path = 'dummy_pbz.pbz'
        dummy_notes_path = 'dummy_notes.txt'
        deploy_pbz_to_pebblefw.deploy_bundle(dummy_pbz_path, 'pebblefw-staging', 'porksmoothie',
                                             dummy_notes_path)

        # Check out mocks to make sure everything worked
        mock_sha256_file.assert_called_with(dummy_pbz_path)

        mock_open.assert_called_with(dummy_notes_path, 'r')

        mock_requests_get.assert_called_with(
                'https://pebblefw-staging.s3.amazonaws.com/pebble/bb2/porksmoothie/latest.json')

        assert mock_boto_connect_s3.called
        expected_new_latest_json = {
            'normal': {
                'url': 'https://pebblefw-staging.s3.amazonaws.com/pebble/bb2/porksmoothie/pbz/' +
                       os.path.basename(dummy_pbz_path),
                'timestamp': self.DUMMY_MANIFEST_CONTENT['timestamp'],
                'notes': dummy_notes,
                'friendlyVersion': self.DUMMY_MANIFEST_CONTENT['versionTag'],
                'sha-256': dummy_sha
            }
        }
        actual_new_latest_json = mock_latest_key.set_contents_from_string.call_args[0][0]

        # Send it through the json loader to normalize any formatting
        actual_new_latest_json = json.dumps(json.loads(actual_new_latest_json))

        nose.tools.eq_(json.dumps(expected_new_latest_json), actual_new_latest_json)