(* Analytics service samples using state monad and functional lenses 
 * Management API: http://code.google.com/apis/analytics/docs/mgmt/v3/mgmtJava.html
 * Core Reporting API: http://code.google.com/apis/analytics/docs/gdata/v3/exportJava.html *)

(*** Setup ***)

open GapiUtils.Infix
open GapiLens.Infix
open GapiLens.StateInfix
open GapiMonad.SessionM
open GapiAnalyticsV3Model
open GapiAnalyticsV3Service

(*** How to configure your client and authenticate using OAuth 2.0 for native
   * applications. ***)

let application_name = "YOUR_APPLICATION_NAME"

(* The clientId and clientSecret are copied from the API Access tab on
 * the Google APIs Console *)
let client_id = "YOUR_CLIENT_ID"

let client_secret = "YOUR_CLIENT_SECRET"

let configuration =
  GapiConfig.default
  |> GapiConfig.application_name ^= application_name
  |> GapiConfig.auth
     ^= GapiConfig.OAuth2 { GapiConfig.client_id; client_secret }

(* Or your redirect URL for web based applications. *)
let redirect_uri = "urn:ietf:wg:oauth:2.0:oob"

let scope = [ GapiAnalyticsV3Service.scope_readonly ]

(* Step 1: Authorize --> *)
let authorization_url =
  GapiOAuth2.authorization_code_url ~redirect_uri ~scope ~response_type:"code"
    client_id

(* Point or redirect your user to the authorization_url. *)
let () =
  print_endline "Go to the following link in your browser:";
  print_endline authorization_url

(* Read the authorization code from the standard input stream. *)
let () = print_endline "What is the authorization code?"

let code = input_line stdin

(* End of Step 1 <-- *)

let batch =
  perform (* Step 2: Exchange --> *) response
  <-- GapiOAuth2.get_access_token ~client_id ~client_secret ~code ~redirect_uri;

  let access_token, refresh_token =
    match response with
    | GapiAuthResponse.OAuth2AccessToken token ->
        ( token.GapiAuthResponse.OAuth2.access_token,
          token.GapiAuthResponse.OAuth2.refresh_token )
    | _ -> failwith "Not supported OAuth2 response"
  in

  (* End of Step 2 <-- *)

  (* Update session with OAuth2 tokens *)
  GapiConversation.Session.auth
  ^=! GapiConversation.Session.OAuth2
        { GapiConversation.Session.oauth2_token = access_token; refresh_token };

  (*** Management API ***)

  (*** Working with accounts ***)
  accounts <-- ManagementResource.Accounts.list;

  let () =
    List.iter
      (fun account ->
        Printf.printf "Account ID: %s\n" account.Account.id;
        Printf.printf "Account Name: %s\n" account.Account.name;
        Printf.printf "Account Created: %s\n"
          (GapiDate.to_string account.Account.created);
        Printf.printf "Account Updated: %s\n"
          (GapiDate.to_string account.Account.updated))
      accounts.Accounts.items
  in

  (*** Working with Web Properties ***)

  (* An application can request a list of the web properties for one of a
   * user's accounts by using the Analytics service object and passing the
   * Account ID to the list method. For example, the following code
   * requests a list of all web properties for the account with ID 12345.
   *)
  accounts <-- ManagementResource.Webproperties.list ~accountId:"12345";

  (* The list method for web properties requires an accountId parameter
   * that specifies which account to use when retrieving the list of web
   * properties. To retrieve the web properties for all of the user's
   * accounts, you can use the wildcard ~all as the accountId, as shown in
   * the following code. *)
  webproperties <-- ManagementResource.Webproperties.list ~accountId:"~all";

  let () =
    List.iter
      (fun webproperty ->
        Printf.printf "Account ID: %s\n" webproperty.Webproperty.accountId;
        Printf.printf "Web Property ID: %s\n" webproperty.Webproperty.id;
        Printf.printf "Web Property Name: %s\n" webproperty.Webproperty.name;
        Printf.printf "Web Property Internal Id: %s\n"
          webproperty.Webproperty.internalWebPropertyId;
        Printf.printf "Web Property Created: %s\n"
          (GapiDate.to_string webproperty.Webproperty.created);
        Printf.printf "Web Property Updated: %s\n"
          (GapiDate.to_string webproperty.Webproperty.updated))
      webproperties.Webproperties.items
  in

  (*** Working with Profiles ***)

  (* To request profiles for a particular account and web property, you
   * specify each ID, as shown in the following example code. *)
  profiles
  <-- ManagementResource.Profiles.list ~accountId:"12345"
        ~webPropertyId:"UA-12345-1";

  (* To request the profiles for all the web properties of a particular
   * account, you can use the ~all wildcard in place of the webPropertyId.
   * The result of the following code is a list of Profiles for all the web
   * properties of the account with ID 12345.*)
  profiles
  <-- ManagementResource.Profiles.list ~accountId:"12345" ~webPropertyId:"~all";

  (* To request all profiles available to the user, you use the ~all
   * wildcard for both ID parameters. The result of the following code is a
   * list of profiles for all the web properties of all the user's
   * accounts. *)
  profiles
  <-- ManagementResource.Profiles.list ~accountId:"~all" ~webPropertyId:"~all";

  (* Note: In all Management API requests, the use of the wildcard ~all is
   * governed by this rule: you cannot specify a particular child ID as a
   * parameter if you used ~all for its parent. For example, the following
   * request is invalid because it passes accountId as the parent of the
   * web property UA-123-1 rather than a specific accountId.

        profiles <-- ManagementResource.Profiles.list
                       ~accountId:"~all"
                       ~webPropertyId:"UA-123-1";
   *)
  let () =
    List.iter
      (fun profile ->
        Printf.printf "Account ID: %s\n" profile.Profile.accountId;
        Printf.printf "Web Property ID: %s\n" profile.Profile.webPropertyId;
        Printf.printf "Web Property Internal Id: %s\n"
          profile.Profile.internalWebPropertyId;
        Printf.printf "Profile ID: %s\n" profile.Profile.id;
        Printf.printf "Profile Name: %s\n" profile.Profile.name;

        Printf.printf "Profile defaultPage: %s\n" profile.Profile.defaultPage;
        Printf.printf "Profile Exclude Query Parameters: %s\n"
          profile.Profile.excludeQueryParameters;
        Printf.printf "Profile Site Search Query Parameters: %s\n"
          profile.Profile.siteSearchQueryParameters;
        Printf.printf "Profile Site Search Category Parameters: %s\n"
          profile.Profile.siteSearchCategoryParameters;

        Printf.printf "Profile Currency: %s\n" profile.Profile.currency;
        Printf.printf "Profile Timezone: %s\n" profile.Profile.timezone;
        Printf.printf "Profile Created: %s\n"
          (GapiDate.to_string profile.Profile.created);
        Printf.printf "Profile Updated: %s\n"
          (GapiDate.to_string profile.Profile.updated))
      profiles.Profiles.items
  in

  (*** Working with Goals ***)

  (* To request goals for a single profile, you specify the ID of an
   * account, web property, and profile, as in the following code. *)
  goals
  <-- ManagementResource.Goals.list ~accountId:"12345"
        ~webPropertyId:"UA-12345-1" ~profileId:"420";

  (* To request all goals for all profiles for the user, you can use the
   * ~all wildcard, as in the following code. *)
  goals
  <-- ManagementResource.Goals.list ~accountId:"~all" ~webPropertyId:"~all"
        ~profileId:"~all";

  (* Note: In all Management API requests, the use of the wildcard ~all is
   * governed by the rule defined in Mixing IDs and wildcard: you cannot
   * specify a particular child ID as a parameter if you used ~all for its
   * parent.
   *
   * For example, the following list request is invalid because it passes
   * the specific profileID 420 but uses the wildcard ~all as the
   * webPropertyId.
        
        goals <-- ManagementResource.Goals.list
                    ~accountId:"~all"
                    ~webPropertyId:"~all"
                    ~profileId:"420";
   *)

  (* URL_DESTINATION Goal *)
  let printGoalUrlDestinationDetails destination =
    let open Goal in
    Printf.printf "Goal URL: %s\n" destination.UrlDestinationDetailsData.url;
    Printf.printf "Case Sensitive: %b\n"
      destination.UrlDestinationDetailsData.caseSensitive;
    Printf.printf "Match Type: %s\n"
      destination.UrlDestinationDetailsData.matchType;
    Printf.printf "First Step Required: %b\n"
      destination.UrlDestinationDetailsData.firstStepRequired;
    if destination.UrlDestinationDetailsData.steps <> [] then (
      print_endline "Goal Steps:";
      List.iter
        (fun step ->
          let open UrlDestinationDetailsData in
          Printf.printf "Step Number: %d\n" step.StepsData.number;
          Printf.printf "Name: %s\n" step.StepsData.name;
          Printf.printf "URL: %s\n" step.StepsData.url)
        destination.UrlDestinationDetailsData.steps )
    else print_endline "No steps configured"
  in

  (* VISIT_TIME_ON_SITE Goal *)
  let printGoalVisitTimeOnSiteDetails time =
    let open Goal in
    print_endline "Goal type: VISIT_TIME_ON_SITE";
    Printf.printf "VISIT_TIME_ON_SITE - Comparison Type: %s\n"
      time.VisitTimeOnSiteDetailsData.comparisonType;
    Printf.printf "VISIT_TIME_ON_SITE - Comparison Value: %s\n"
      time.VisitTimeOnSiteDetailsData.comparisonValue
  in

  (* VISIT_NUM_PAGES Goal *)
  let printGoalVisitNumPagesDetails pages =
    let open Goal in
    print_endline "Goal type: VISIT_NUM_PAGES";
    Printf.printf "VISIT_NUM_PAGES - Comparison Type: %s\n"
      pages.VisitNumPagesDetailsData.comparisonType;
    Printf.printf "VISIT_NUM_PAGES - Comparison Value: %s\n"
      pages.VisitNumPagesDetailsData.comparisonValue
  in

  (* EVENT Goal *)
  let printGoalEventDetails event =
    let open Goal in
    print_endline "Goal type: EVENT";
    Printf.printf "EVENT - Use Event Value: %b\n"
      event.EventDetailsData.useEventValue;
    if event.EventDetailsData.eventConditions <> [] then (
      print_endline "Goal Conditions:";
      List.iter
        (fun conditions ->
          let open EventDetailsData in
          Printf.printf "Type: %s\n" conditions.EventConditionsData._type;
          if conditions.EventConditionsData._type = "VALUE" then (
            Printf.printf "Comparison Type: %s\n"
              conditions.EventConditionsData.comparisonType;
            Printf.printf "Comparison Value: %s\n"
              conditions.EventConditionsData.comparisonValue )
          else (
            Printf.printf "matchType: %s\n"
              conditions.EventConditionsData.matchType;
            Printf.printf "expression: %s\n"
              conditions.EventConditionsData.expression ))
        event.EventDetailsData.eventConditions )
  in

  let () =
    List.iter
      (fun goal ->
        Printf.printf "Account ID: %s\n" goal.Goal.accountId;
        Printf.printf "Web Property ID: %s\n" goal.Goal.webPropertyId;
        Printf.printf "Web Property Internal Id: %s\n"
          goal.Goal.internalWebPropertyId;
        Printf.printf "Profile ID: %s\n" goal.Goal.profileId;

        Printf.printf "Goal Number: %s\n" goal.Goal.id;
        Printf.printf "Goal Name: %s\n" goal.Goal.name;
        Printf.printf "Goal Active: %b\n" goal.Goal.active;
        Printf.printf "Goal Value: %f\n" goal.Goal.value;
        Printf.printf "Goal Type: %s\n" goal.Goal._type;
        Printf.printf "Goal Created: %s\n"
          (GapiDate.to_string goal.Goal.created);
        Printf.printf "Goal Updated: %s\n"
          (GapiDate.to_string goal.Goal.updated);
        printGoalUrlDestinationDetails goal.Goal.urlDestinationDetails;
        printGoalVisitTimeOnSiteDetails goal.Goal.visitTimeOnSiteDetails;
        printGoalVisitNumPagesDetails goal.Goal.visitNumPagesDetails;
        printGoalEventDetails goal.Goal.eventDetails)
      goals.Goals.items
  in

  (*** Working with Advanced Segments ***)
  segments <-- ManagementResource.Segments.list;

  let () =
    List.iter
      (fun segment ->
        Printf.printf "Advanced Segment ID: %s\n" segment.Segment.id;
        Printf.printf "Advanced Segment Name: %s\n" segment.Segment.name;
        Printf.printf "Advanced Segment Definition: %s\n"
          segment.Segment.definition;

        if segment.Segment.created <> GapiDate.epoch then (
          Printf.printf "Advanced Segment Created: %s\n"
            (GapiDate.to_string segment.Segment.created);
          Printf.printf "Advanced Segment Updated: %s\n"
            (GapiDate.to_string segment.Segment.updated) ))
      segments.Segments.items
  in

  (*** Retrieving Analytics data ***)
  ga_data
  <-- DataResource.Ga.get ~ids:"ga:420" (* "ga:" + Profile ID *)
        ~start_date:"2011-09-01" ~end_date:"2011-09-30"
        ~metrics:"ga:visits,ga:pageviews" ~dimensions:"ga:source,ga:medium"
        ~filters:"ga:medium==referral" ~sort:"-ga:visit" ~segment:"gaid::-11"
        ~max_results:50;

  (*** Working with Analytics data ***)

  (* ColumnHeader data *)
  let print_column_header data =
    print_endline "Column Headers:";
    List.iter
      (fun header ->
        let open GaData in
        Printf.printf "Column Name: %s\n" header.ColumnHeadersData.name;
        Printf.printf "Column Type: %s\n" header.ColumnHeadersData.columnType;
        Printf.printf "Column Data Type: %s\n" header.ColumnHeadersData.dataType)
      data.GaData.columnHeaders
  in

  (* Row data *)
  let print_table_data data =
    if data.GaData.totalResults > 0 then (
      print_endline "Data Table:";

      (* Print the column names. *)
      List.iter
        (fun header ->
          let open GaData in
          Printf.printf "%-32s"
            ( header.ColumnHeadersData.name ^ "("
            ^ header.ColumnHeadersData.dataType ^ ")" ))
        data.GaData.columnHeaders;
      print_newline ();

      (* Print the rows of data. *)
      List.iter
        (fun row_values ->
          List.iter (fun value -> Printf.printf "%-32s" value) row_values;
          print_newline ())
        data.GaData.rows )
    else print_endline "No data"
  in

  (* Report information *)
  let print_response_info data =
    print_newline ();
    print_endline "Response:";
    Printf.printf "ID: %s\n" data.GaData.id;
    Printf.printf "Self link: %s\n" data.GaData.selfLink;
    Printf.printf "Kind: %s\n" data.GaData.kind;
    Printf.printf "Contains Sampled Data: %b\n" data.GaData.containsSampledData
  in

  (* Profile information *)
  let print_profile_info data =
    let profile_info = data.GaData.profileInfo in

    print_endline "Profile Info";
    let open GaData in
    Printf.printf "Account ID: %s\n" profile_info.ProfileInfoData.accountId;
    Printf.printf "Web Property ID: %s\n"
      profile_info.ProfileInfoData.webPropertyId;
    Printf.printf "Internal Web Property ID: %s\n"
      profile_info.ProfileInfoData.internalWebPropertyId;
    Printf.printf "Profile ID: %s\n" profile_info.ProfileInfoData.profileId;
    Printf.printf "Profile Name: %s\n" profile_info.ProfileInfoData.profileName;
    Printf.printf "Table ID: %s\n" profile_info.ProfileInfoData.tableId
  in

  (* Query information *)
  let print_query_info data =
    let query_info = data.GaData.query in

    print_endline "Query Info:";
    let open GaData in
    Printf.printf "Ids: %s\n" query_info.QueryData.ids;
    Printf.printf "Start Date: %s\n" query_info.QueryData.start_date;
    Printf.printf "End Date: %s\n" query_info.QueryData.end_date;
    Printf.printf "Metrics: %s\n"
      (String.concat "," query_info.QueryData.metrics);
    Printf.printf "Dimensions: %s\n" query_info.QueryData.dimensions;
    Printf.printf "Sort: %s\n" (String.concat "," query_info.QueryData.sort);
    Printf.printf "Segment: %s\n" query_info.QueryData.segment;
    Printf.printf "Filters: %s\n" query_info.QueryData.filters;
    Printf.printf "Start Index: %d\n" query_info.QueryData.start_index;
    Printf.printf "Max Results: %d\n" query_info.QueryData.max_results
  in

  (* Pagination information *)
  let print_pagination_info data =
    print_endline "Pagination Info:";
    Printf.printf "Previous Link: %s\n" data.GaData.previousLink;
    Printf.printf "Next Link: %s\n" data.GaData.nextLink;
    Printf.printf "Items Per Page: %d\n" data.GaData.itemsPerPage;
    Printf.printf "Total Results: %d\n" data.GaData.totalResults
  in

  (* Totals for all results *)
  let print_totals_for_all_results data =
    print_endline "Metric totals over all results:";
    List.iter
      (fun (key, value) -> Printf.printf "%s: %s\n" key value)
      data.GaData.totalsForAllResults
  in

  let () =
    print_column_header ga_data;
    print_table_data ga_data;
    print_response_info ga_data;
    print_profile_info ga_data;
    print_query_info ga_data;
    print_pagination_info ga_data;
    print_totals_for_all_results ga_data
  in

  (* ... *)
  return ()

(* Start a new session *)
let _ = GapiConversation.with_curl configuration batch
