import IMAPISerializer from 'web-client/serializers/im-api';
import threadLastMessages from 'web-client/utilities/thread-last-messages';

const BASE_URL = '/v1/threads';

export default IMAPISerializer.extend({

  attrs: {
    lastReadId: 'last_read',
    lastUpdatedAt: 'last_updated',
    isMuted: 'muted',
    isArchived: 'archived'
  },

  /*
  Server Response:
  {
    data: [{
      archived: false,
      id: 1234_4567,
      last_message: <message>
      last_read: 5,
      last_updated: 1441234151, // Timestamp with second precision
      muted: false,
      participants: [{
        color: '#fff',
        display_name: 'Extalix',
        id: 1234,
        turbo: true,
        user_type: 'staff',
        username: 'extalix'
      }, { ... // Second Participant }]
    },
    ... // More threads
    ]
  }
  Normalized to:
  {
    included: [{
      type: 'participant',
      id: 1234,
      attributes: {
        color: '#fff',
        display_name: 'Extalix',
        user_type: 'staff',
        username: 'extalix',
        has_turbo: true
      },
      relationships: {
        thread: {
          data: {type: 'thread', id: 1234_4567}
        }
      },
      links: {
        self: '/threads/1234_4567/participants/1234'
      }
    },
    { ... // Second Participant },
    ],
    data: [{
      type: 'thread',
      id: 1234_4567,
      links: {
        self: '/threads/1234_4567'
      },
      attributes: {
        last_read: 5,
        archived: false,
        muted: false,
        last_updated: 1441234151000,
        isLocal: false
      },
      relationships: {
        participants: {
          data: [{type: 'participant', id: 1234}, {type: 'participant', id: 4567}],
        }
      }
    },
    ... // More threads
    ]
  }

  Single thread serialization is similar to multi thread serializatoin, but for one thread instead of an array.
  */

  normalizeFindAllResponse(store, modelClass, payload) {
    payload.included = [];
    payload.data.forEach(thread => {
      thread.links = {
        self: `${payload.links.self}/${thread.id}`
      };
      thread.type = 'thread';
      Array.prototype.push.apply(payload.included, this._buildIncluded(thread));
    });

    payload.meta = {
      'total': payload.total
    };

    return this._super(...arguments);
  },

  normalizeQueryResponse(store, modelClass, payload) {
    let lastMessages = {};

    payload.included = [];
    if (payload.data) {
      payload.data.forEach(thread => {
        lastMessages[thread.id] = thread.last_message;

        thread.links = {
          self: `${payload.links.self}/${thread.id}`
        };
        thread.type = 'thread';
        Array.prototype.push.apply(payload.included, this._buildIncluded(thread));
      });
    }

    // HACK(mikeche): Since the threads API can return the same participant with different user
    // data (e.g., if the user changed their chat color), we reverse the array so that the most
    // recent data is the one that sticks. This mitigates the issue somewhat.
    // This can be removed once we move to Visage as the denormalization of participants will be
    // done at that layer.
    // See https://twitchtv.atlassian.net/browse/MES-860
    payload.included.reverse();

    payload.meta = {
      'total': payload.total,
      'lastMessages': lastMessages
    };

    return this._super(...arguments);
  },

  _normalizeSingleThreadPayload(payload) {
    payload.type = 'thread';
    let normalized = {
      data: payload,
      included: this._buildIncluded(payload),
      links: payload.links
    };

    return normalized;
  },

  normalizeSingleResponse(store, modelClass, payload, id, requestType) {
    threadLastMessages[id] = payload.last_message;
    let normalized = this._normalizeSingleThreadPayload(payload);
    return this._super(store, modelClass, normalized, id, requestType);
  },

  normalize(typeClass, hash) {
    let data = this._buildThreadData(hash);
    return this._super(typeClass, data);
  },

  serialize(record) {
    let changed = record.changedAttributes(),
        updates = {};
    Object.keys(changed).forEach(key => {
      updates[this.keyForAttribute(key)] = changed[key][1];
    });
    return updates;
  },

  createDummyThreadForUser(threadId, currentUserData, otherUserData) {
    return {
      data: {
        type: 'thread',
        id: threadId,
        links: {self: `${BASE_URL}/${threadId}`},
        attributes: {
          lastReadId: 0,
          isArchived: false,
          isMuted: false,
          lastUpdatedAt: new Date(),
          isLocal: true,
          lastMarkedNotSpam: 0,
          spamLikelihood: 'low'
        },
        relationships: {
          messages: {
            links: {
              related: `${BASE_URL}/${threadId}/messages`
            }
          },
          participants: {
            data: [
              {type: 'participant', id: currentUserData.id},
              {type: 'participant', id: otherUserData.id}
            ],
            links: {
              related: `${BASE_URL}/${threadId}/participants`
            }
          }
        }
      },
      included: [
        {
          type: 'participant',
          id: currentUserData.id,
          attributes: {
            displayName: currentUserData.name,
            username: currentUserData.login
          }
        },
        {
          type: 'participant',
          id: otherUserData.id,
          attributes: {
            displayName: otherUserData.name,
            username: otherUserData.login
          },
          relationships: {
            thread: {
              data: {type: 'thread', id: threadId}
            }
          },
          links: {
            self: `${BASE_URL}/${threadId}/participants/${otherUserData.id}`
          }
        }
      ]
    };
  },

  _buildAttributes(hash) {
    return {
      last_read: hash.last_read,
      archived: hash.archived,
      muted: hash.muted,
      last_updated: hash.last_updated * 1000,
      isLocal: false,
      last_marked_not_spam: hash.spam_info.last_marked_not_spam,
      spam_likelihood: hash.spam_info.likelihood
    };
  },

  _buildThreadData(hash) {
    return {
      type: 'thread',
      id: hash.id,
      links: hash.links,
      attributes: this._buildAttributes(hash),
      relationships: this._buildRelationships(hash)
    };
  },

  _buildRelationships(hash) {
    let relationships = {};

    relationships.participants = {
      data: []
    };

    hash.participants.forEach(participant => {
      relationships.participants.data.push({type: 'participant', id: participant.id});
    });

    return relationships;
  },

  _buildIncluded(thread) {
    let included = [];
    // Add participants to included array
    thread.participants.forEach(participant => {
      included.push({
        type: 'participant',
        id: participant.id,
        attributes: {
          badges: participant.badges,
          color: participant.color,
          display_name: participant.display_name,
          user_type: participant.user_type,
          username: participant.username,
          has_turbo: participant.turbo,
          profile_image: participant.profile_image
        },
        relationships: {
          thread: {
            data: {type: 'thread', id: thread.id}
          }
        },
        links: {
          self: `${thread.links.self}/participants/${participant.id}`
        }
      });
    });

    return included;
  }
});
