import { Component } from "@angular/core";
import {
  IonicPage,
  NavController,
  NavParams,
  LoadingController,
  AlertController
} from "ionic-angular";
import {
  FormBuilder,
  Validators,
  FormGroup,
  ValidatorFn,
  AbstractControl
} from "@angular/forms";
import { MockProvider } from "../../providers/mock-provider/mock-provider";
import { TwitterApiProvider } from "../../providers/twitter-api/twitter-api";
import { Storage } from "@ionic/storage";
import { P2pStorageIpfsProvider } from "../../providers/p2p-storage-ipfs/p2p-storage-ipfs";
import { P2pDatabaseGunProvider } from "../../providers/p2p-database-gun/p2p-database-gun";
import twittertext from "twitter-text";
import { CryptoProvider } from "../../providers/crypto/crypto";

@IonicPage()
@Component({
  selector: "page-write-tweet",
  templateUrl: "write-tweet.html"
})
export class WriteTweetPage {
  tweet: FormGroup;
  retweetId: string;
  replyToStatusId: string;
  retweet;
  replyTweet;

  constructor(
    public navCtrl: NavController,
    public navParams: NavParams,
    private formBuilder: FormBuilder,
    private twitter: TwitterApiProvider,
    private loadingCtrl: LoadingController,
    private storage: Storage,
    private ipfs: P2pStorageIpfsProvider,
    private gun: P2pDatabaseGunProvider,
    private cryptoUtils: CryptoProvider,
    private alertCtrl: AlertController,
    private mockProv: MockProvider
  ) {
    this.retweetId = this.navParams.get("tweetId");
    this.replyToStatusId = this.navParams.get("replyToStatus");
    console.log('retweeting now? retweetId:', this.retweetId, 'replyToStatusId: ', this.replyToStatusId);

    this.tweet = this.formBuilder.group({
      text: [""],
      p2p: [false]
    });

    this.addValidators();
  }

  private async addValidators() {
    const triggerWords = await this.storage.get("keywords");
    const validators = [
      Validators.maxLength(140),
      this.containsTriggerWord(triggerWords)
    ];
    this.tweet.controls["text"].setValidators(validators);
  }

  private containsTriggerWord(triggerWords: string): ValidatorFn {
    return (control: AbstractControl): {
      [key: string]: any
    } | null => {
      if (triggerWords) {
        const regexList = triggerWords
          .toLowerCase()
          .split(", ")
          .join("|");
        const regex = new RegExp(regexList);
        const containsTriggerWord = regex.test(control.value.toLowerCase());
        return containsTriggerWord ? { containsTriggerWord: { value: control.value } } :
          null;
      } else {
        return null;
      }
    };
  }

  async ionViewDidLoad() {
    if (this.retweetId) {
      if (this.storage.get("mockup")) {
        this.retweet = this.mockProv.fetchTweet(this.retweetId);
        console.log("retweeted tweet", this.retweet);
      } else {
        this.retweet = await this.twitter.fetchTweet(this.retweetId);
      }
    }
    if (this.replyToStatusId) {
      if (this.storage.get("mockup")){
        console.log("replying in mockup");
        this.replyTweet = await this.mockProv.fetchTweet(this.replyToStatusId);
      }
      else
        this.replyTweet = await this.twitter.fetchTweet(this.replyToStatusId);
      console.log('this.replyTweet is:', this.replyTweet);
    }
  }

  get tweetCharProgress() {
    const progress = 1 - this.tweet.value["text"].length / 140;
    const radius = 8;
    const circumference = Math.PI * radius * 2;
    return progress * circumference;
  }

  get showTrigger(): boolean {
    return (
      this.tweet &&
      this.tweet.controls &&
      this.tweet.controls.text &&
      this.tweet.controls.text.errors &&
      this.tweet.controls.text.errors["containsTriggerWord"] &&
      !this.tweet.controls.p2p.value
    );
  }

  showTriggerInfo() {
    this.alertCtrl
      .create({
        title: "Watch Out!",
        message: "Your tweet contains words you have previously defined to only share securely via P2P. Currently P2P mode is not selected.",
        buttons: ["OK"]
      })
      .present();
  }

  async submitTweet() {
    const loading = this.loadingCtrl.create();
    loading.present();
    if (await this.storage.get("mockup")) {
      console.log('value of the tweet is:', this.tweet.value);
      await this.mockProv.writeTweet(this.tweet.value["text"],
        this.tweet.value.p2p,
        this.retweet,
        this.replyTweet);
    } else {
      if (this.tweet.value.p2p) {
        loading.setContent("Validate keys...");
        if (
          (await this.cryptoUtils.isPrivateKeySet()) &&
          (await this.cryptoUtils.isPublicKeyPublished())
        ) {
          loading.setContent("Publish private tweet...");
          await this.tweetPrivate();
        } else {
          loading.dismiss();
          const alert = this.alertCtrl.create({
            title: "Oooops...",
            message: "Please verify that you have set a private and public key in the settings and that your latest public key was published."
          });
          alert.present();
          return;
        }
      } else {
        loading.setContent("Publish on Twitter...");
        await this.twitter.tweet(
          this.tweet.value["text"],
          this.retweet,
          this.replyToStatusId
        );
      }
    }
    loading.dismiss();
    this.navCtrl.pop();
  }

  private async tweetPrivate() {
    const tweet = await this.buildPrivateTweet();
    const privateKey = await this.storage.get("privateKey");
    const encryptedTweet = this.cryptoUtils.encrypt(
      JSON.stringify(tweet),
      privateKey
    );
    const res = await this.ipfs.storeTweet(encryptedTweet);

    this.gun.storeLastTweetHashForUser(
      tweet.user_id,
      res["Hash"],
      tweet.created_at
    );

    this.gun.publishHashtags(tweet.entities.hashtags);
  }

  private async buildPrivateTweet() {
    console.log('building private tweet');
    const status = this.tweet.value["text"].trim();
    const entities = await this.getEntities(status);

    return {
      full_text: status,
      user_id: await this.storage.get("userId"),
      created_at: Date.now(),
      private_tweet: true,
      in_reply_to_status_id: this.replyToStatusId,
      quoted_status_id: this.retweetId,
      display_text_range: [0, status.length],
      entities: entities
    };
  }

  private async getEntities(status: string) {
    return {
      hashtags: twittertext.extractHashtagsWithIndices(status),
      urls: twittertext.extractUrlsWithIndices(status),
      user_mentions: await this.getMentions(status)
    };
  }

  private async getMentions(status: string) {
    // extract mentions
    const entities = twittertext.extractMentionsWithIndices(status);

    // add user_id
    const entitiesWithPromises = entities.map(async mention => {
      try {
        const user = await this.twitter.fetchUserFromScreenName(
          mention.screenName
        );
        mention["id_str"] = user[0]["id_str"];
        mention["screen_name"] = mention.screenName;
        delete mention.screenName;
      } catch (err) {
        console.error(
          "There is no user signed up to twitter with username: " +
          mention.screenName
        );
      }
      return mention;
    });

    // filter for valid users and return
    return (await Promise.all(entitiesWithPromises)).filter(el =>
      el.hasOwnProperty("id_str")
    );
  }
}
