import { Logging } from 'homebridge';
import querystring from 'querystring';
import { AxiosRequestConfig } from 'axios';
import axios from 'axios';
import { FordpassConfig } from './types/config';

const authUrl = 'https://sso.ci.ford.com/';
const vehiclesUrl = 'https://services.cx.ford.com/api/dashboard/v1/users/vehicles';
const defaultAppId = '71A3AD0A-CF46-4CCF-B473-FC7FE5BC4592';
const clientId = '9fb503e0-715b-47e8-adfd-ad4b7770f73b';
const userAgent = 'FordPass/5 CFNetwork/1325.0.1 Darwin/21.1.0';

export class Connection {
  private config: FordpassConfig;
  private readonly log: Logging;
  private applicationId: string;

  constructor(config: FordpassConfig, log: Logging) {
    this.config = config;
    this.log = log;
    this.applicationId = config.options?.region || defaultAppId;
  }

  async auth(): Promise<any> {
    const url = authUrl + 'oidc/endpoint/default/token';
    const options: AxiosRequestConfig = {
      method: 'POST',
      url: url,
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'User-Agent': userAgent,
      },
      data: querystring.stringify({
        client_id: clientId,
        grant_type: 'password',
        username: this.config.username,
        password: this.config.password,
      }),
    };

    try {
      const result = await axios(options);
      if (result.status === 200 && result.data.access_token) {
        const nextResult = await axios.put(
          'https://api.mps.ford.com/api/oauth2/v1/token',
          {
            code: result.data.access_token,
          },
          {
            headers: {
              'Content-Type': 'application/json',
              'User-Agent': userAgent,
              'Application-Id': this.applicationId,
            },
          },
        );
        if (nextResult.status === 200 && nextResult.data.access_token) {
          this.config.access_token = nextResult.data.access_token;
          this.config.refresh_token = nextResult.data.refresh_token;
          return nextResult.data;
        } else {
          this.log.error(`Auth failed with status: ${nextResult.status}`);
        }
      } else {
        this.log.error(`Auth failed with status: ${result.status}`);
      }
      return;
    } catch (error: any) {
      this.log.error(`Auth failed with error: ${error.code || error.response.status}`);
      return;
    }
  }

  async refreshAuth(): Promise<any> {
    try {
      if (this.config.refresh_token) {
        const result = await axios.put(
          'https://api.mps.ford.com/api/oauth2/v1/refresh',
          {
            refresh_token: this.config.refresh_token,
          },
          {
            headers: {
              'Content-Type': 'application/json',
              'User-Agent': userAgent,
              'Application-Id': this.applicationId,
            },
          },
        );
        if (result.status === 200 && result.data.access_token) {
          this.config.access_token = result.data.access_token;
          this.config.refresh_token = result.data.refresh_token;
          return result.data;
        } else {
          this.log.error(`Auth failed with status: ${result.status}`);
        }
      } else {
        return await this.auth();
      }
      return;
    } catch (error: any) {
      this.log.error(`Auth failed with error: ${error.code || error.response.status}`);
      return;
    }
  }

  async getVehicles(): Promise<Array<any>> {
    if (!this.config.access_token) {
      return [];
    }
    const options: AxiosRequestConfig = {
      method: 'GET',
      url: vehiclesUrl,
      headers: {
        'Content-Type': 'application/json',
        'User-Agent': userAgent,
        'Auth-Token': this.config.access_token,
        'Application-Id': this.applicationId,
      },
    };

    try {
      const result = await axios(options);
      if (result.status === 200 && result.data) {
        return result.data;
      } else {
        this.log.error(`Vehicles failed with status: ${result.status}`);
      }
      return [];
    } catch (error: any) {
      this.log.error(`Vehicles failed with error: ${error.code || error.response.status}`);
      return [];
    }
  }
}