/**
 * Copyright 2019 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
 *
 *     https://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.
 */

'use strict';

const functions = require('firebase-functions');
const {smarthome} = require('actions-on-google');
const {google} = require('googleapis');
const util = require('util');
const admin = require('firebase-admin');
// Initialize Firebase
admin.initializeApp();
const firebaseRef = admin.database().ref('/');
// Initialize Homegraph



const auth = new google.auth.GoogleAuth({
  scopes: ['https://www.googleapis.com/auth/homegraph'],
});
const homegraph = google.homegraph({
  version: 'v1',
  auth: auth,
});

/*const homegraph = homegraph({
  version: 'v1',
  auth: new GoogleAuth({
    scopes: 'https://www.googleapis.com/auth/homegraph'
  })
});*/

// Hardcoded user ID
const USER_ID = '123';

exports.login = functions.https.onRequest((request, response) => {
  if (request.method === 'GET') {
    functions.logger.log('Requesting login page');
    response.send(`
    <html>
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <body>
        <form action="/login" method="post">
          <input type="hidden"
            name="responseurl" value="${request.query.responseurl}" />
          <button type="submit" style="font-size:14pt">
            Link this service to Google
          </button>
        </form>
      </body>
    </html>
  `);
  } else if (request.method === 'POST') {
    // Here, you should validate the user account.
    // In this sample, we do not do that.
    const responseurl = decodeURIComponent(request.body.responseurl);
    functions.logger.log(`Redirect to ${responseurl}`);
    return response.redirect(responseurl);
  } else {
    // Unsupported method
    response.send(405, 'Method Not Allowed');
  }
});

exports.fakeauth = functions.https.onRequest((request, response) => {
  const responseurl = util.format('%s?code=%s&state=%s',
      decodeURIComponent(request.query.redirect_uri), 'xxxxxx',
      request.query.state);
  functions.logger.log(`Set redirect as ${responseurl}`);
  return response.redirect(
      `/login?responseurl=${encodeURIComponent(responseurl)}`);
});

exports.faketoken = functions.https.onRequest((request, response) => {
  const grantType = request.query.grant_type ?
    request.query.grant_type : request.body.grant_type;
  const secondsInDay = 86400; // 60 * 60 * 24
  const HTTP_STATUS_OK = 200;
  functions.logger.log(`Grant type ${grantType}`);

  let obj;
  if (grantType === 'authorization_code') {
    obj = {
      token_type: 'bearer',
      access_token: '123access',
      refresh_token: '123refresh',
      expires_in: secondsInDay,
    };
  } else if (grantType === 'refresh_token') {
    obj = {
      token_type: 'bearer',
      access_token: '123access',
      expires_in: secondsInDay,
    };
  }
  response.status(HTTP_STATUS_OK)
      .json(obj);
});

const app = smarthome();

app.onSync((body) => {
  return {
    requestId: body.requestId,
    payload: {
      agentUserId: USER_ID,
      devices: [{
        id: 'washer',
        type: 'action.devices.types.WASHER',
        traits: [
          'action.devices.traits.OnOff',
          'action.devices.traits.StartStop',
          'action.devices.traits.RunCycle',
        ],
        name: {
          defaultNames: 'My Washer',
          name: 'Washer',
          nicknames: 'Washer',
        },
        deviceInfo: {
          manufacturer: 'Adam Silar',
          model: 'WasherTest',
          hwVersion: '1.0',
          swVersion: '1.0.1',
        },
        willReportState: true,
        attributes: {
          pausable: true,
        },
        otherDeviceIds: {
          deviceId: 'deviceid123',
        },
      },
      {
        id: 'rgblight',
        type: 'action.devices.types.LIGHT',
        traits: [
          'action.devices.traits.OnOff',
          'action.devices.traits.ColorSetting',
        ],
        name: {
          defaultNames: 'RGB Light',
          name: 'RGB Light',
          nicknames: 'RGB Light',
        },
        deviceInfo: {
          manufacturer: 'Adam Silar',
          model: 'RGBLightTest',
          hwVersion: '1.0',
          swVersion: '1.0.1',
        },
        willReportState: true,
        attributes: {
          colorModel: 'rgb',
        },
        otherDeviceIds: {
          deviceId: 'rgblight123',  
        },
      }],
    },
  };
});

const queryFirebase = async (deviceId) => {
  const snapshot = await firebaseRef.child(deviceId).once('value');
  const snapshotVal = snapshot.val();

  var qfvalue = {};

  if (Object.prototype.hasOwnProperty.call(snapshotVal, 'OnOff')) {
    qfvalue = Object.assign(qfvalue, {on: snapshotVal.OnOff.on});
  }
  if (Object.prototype.hasOwnProperty.call(snapshotVal, 'StartStop')) {
    qfvalue = Object.assign(qfvalue, {isPaused: snapshotVal.StartStop.isPaused});
  }
  if (Object.prototype.hasOwnProperty.call(snapshotVal, 'StartStop')) {
    qfvalue = Object.assign(qfvalue, {isRunning: snapshotVal.StartStop.isRunning});
  }
  if (Object.prototype.hasOwnProperty.call(snapshotVal, 'ColorSetting')) {
    qfvalue = Object.assign(qfvalue, {color: snapshotVal.ColorSetting.color});
  }
  return qfvalue;
  /*
  return {
    on: snapshotVal.OnOff.on,
    isPaused: snapshotVal.StartStop.isPaused,
    isRunning: snapshotVal.StartStop.isRunning,
    color: snapshotVal.ColorSetting.color,
  };*/
};
const queryDevice = async (deviceId) => {
  const data = await queryFirebase(deviceId);

  var qdvalue = {};

  if (Object.prototype.hasOwnProperty.call(data, 'on')) {
    qdvalue = Object.assign(qdvalue, {on: data.on});
  }
  if (Object.prototype.hasOwnProperty.call(data, 'isPaused')) {
    qdvalue = Object.assign(qdvalue, {isPaused: data.isPaused});
  }
  if (Object.prototype.hasOwnProperty.call(data, 'isRunning')) {
    qdvalue = Object.assign(qdvalue, {isRunning: data.isRunning});
  }
  if (Object.prototype.hasOwnProperty.call(data, 'color')) {
    qdvalue = Object.assign(qdvalue, {color: data.color});
  }
  return qdvalue;

};

app.onQuery(async (body) => {
  const {requestId} = body;
  const payload = {
    devices: {},
  };
  const queryPromises = [];
  const intent = body.inputs[0];
  for (const device of intent.payload.devices) {
    const deviceId = device.id;
    queryPromises.push(queryDevice(deviceId)
        .then((data) => {
        // Add response to device payload
          payload.devices[deviceId] = data;
        },
        ));
  }
  // Wait for all promises to resolve
  await Promise.all(queryPromises);
  return {
    requestId: requestId,
    payload: payload,
  };
});

const updateDevice = async (execution, deviceId) => {
  const {params, command} = execution;
  let state; let ref;
  switch (command) {
    case 'action.devices.commands.OnOff':
      state = {on: params.on};
      ref = firebaseRef.child(deviceId).child('OnOff');
      break;
    case 'action.devices.commands.StartStop':
      state = {isRunning: params.start};
      ref = firebaseRef.child(deviceId).child('StartStop');
      break;
    case 'action.devices.commands.PauseUnpause':
      state = {isPaused: params.pause};
      ref = firebaseRef.child(deviceId).child('StartStop');
      break;
    case 'action.devices.commands.ColorAbsolute':
      state = {color: params.color};
      ref = firebaseRef.child(deviceId).child('ColorSetting')
  }

  return ref.update(state)
      .then(() => state);
};

app.onExecute(async (body) => {
  const {requestId} = body;
  // Execution results are grouped by status
  const result = {
    ids: [],
    status: 'SUCCESS',
    states: {
      online: true,
    },
  };

  const executePromises = [];
  const intent = body.inputs[0];
  for (const command of intent.payload.commands) {
    for (const device of command.devices) {
      for (const execution of command.execution) {
        executePromises.push(
            updateDevice(execution, device.id)
                .then((data) => {
                  result.ids.push(device.id);
                  Object.assign(result.states, data);
                })
                .catch(() => functions.logger.error('EXECUTE', device.id)),
        );
      }
    }
  }

  await Promise.all(executePromises);
  return {
    requestId: requestId,
    payload: {
      commands: [result],
    },
  };
});

app.onDisconnect((body, headers) => {
  functions.logger.log('User account unlinked from Google Assistant');
  // Return empty response
  return {};
});

exports.smarthome = functions.https.onRequest(app);

exports.requestsync = functions.https.onRequest(async (request, response) => {
  response.set('Access-Control-Allow-Origin', '*');
  functions.logger.info(`Request SYNC for user ${USER_ID}`);
  try {
    const res = await homegraph.devices.requestSync({
      requestBody: {
        agentUserId: USER_ID,
      },
    });
    functions.logger.info('Request sync response:', res.status, res.data);
    response.json(res.data);
  } catch (err) {
    functions.logger.error(err);
    response.status(500).send(`Error requesting sync: ${err}`);
  }
});

/**
 * Send a REPORT STATE call to the homegraph when data for any device id
 * has been changed.
 */
exports.reportstate = functions.database.ref('{deviceId}').onWrite(
    async (change, context) => {
      functions.logger.info('Firebase write event triggered Report State');
      const snapshot = change.after.val();

      var rsvalue = {};

      if (Object.prototype.hasOwnProperty.call(snapshot, 'OnOff')) {
        rsvalue = Object.assign(rsvalue, {on: snapshot.OnOff.on});
      }
      if (Object.prototype.hasOwnProperty.call(snapshot, 'StartStop')) {
        rsvalue = Object.assign(rsvalue, {isPaused: snapshot.StartStop.isPaused});
      }
      if (Object.prototype.hasOwnProperty.call(snapshot, 'StartStop')) {
        rsvalue = Object.assign(rsvalue, {isRunning: snapshot.StartStop.isRunning});
      }
      if (Object.prototype.hasOwnProperty.call(snapshot, 'ColorSetting')) {
        rsvalue = Object.assign(rsvalue, {color: snapshot.ColorSetting.color});
      }

      const requestBody = {
        requestId: 'ff36a3cc', // Any unique ID 
        agentUserId: USER_ID,
        payload: {
          devices: {
            states: {
              [context.params.deviceId]: rsvalue,
            },
          },
        },
      };

      const res = await homegraph.devices.reportStateAndNotification({
        requestBody
      });
      functions.logger.info('Report state response:', res.status, res.data);
    });

/**
 * Update the current state of the washer device
 */
exports.updatestate = functions.https.onRequest((request, response) => {
  firebaseRef.child('washer').update({
    OnOff: {
      on: request.body.on,
    },
    StartStop: {
      isPaused: request.body.isPaused,
      isRunning: request.body.isRunning,
    },
  });

  firebaseRef.child('rgblight').update({
    OnOff: {
      on: request.body.on,
    },
    ColorSetting: {
      color: request.body.color,
    },
  });

  return response.status(200).end();
});
