package datamysql

import (
	"database/sql"
	"fmt"

	"bitbucket.org/nemt/nemt-portal-api/domain/entity"
	"bitbucket.org/nemt/nemt-portal-api/infra/errors"
	uuid "github.com/satori/go.uuid"
)

// visitRepo maps methods to database
type notificationRepo struct {
	conn executor
}

func newNotificationRepo(conn executor) *notificationRepo {
	return &notificationRepo{
		conn: conn,
	}
}

func (c *notificationRepo) Create(notification entity.Notification) (entity.Notification, error) {
	const (
		statusQuery = `SELECT a.contact_type_id, b.ride_id, c.user_id, d.user_id created_user_id FROM tab_contact_type    a,        tab_ride            b, tab_user            c, tab_user            d WHERE a.key = ? AND  b.ride_uuid = ? AND  c.user_uuid = ? AND  d.user_uuid = ?;`
		query       = `INSERT INTO tab_notification(notification_uuid, ride_id, user_id, contact_type_id, contact, message, created_user_id, readed, notificaton_type) VALUES(?, ?, ?, ?, ?, ?, ?, 0, ?);`
	)

	retVal := notification
	UUID, _ := uuid.NewV4()
	retVal.UUID = UUID.String()

	row := c.conn.QueryRow(statusQuery, retVal.Type, retVal.Ride.UUID, retVal.User.UUID, retVal.CreatedUser.UUID)
	if err := row.Scan(&retVal.TypeID, &retVal.Ride.ID, &retVal.User.ID, &retVal.CreatedUser.ID); err != nil {
		fmt.Println("Error to get base data: ", err.Error())
		return retVal, err
	}

	results, err := c.conn.Exec(query, retVal.UUID, retVal.Ride.ID, retVal.User.ID, retVal.TypeID, retVal.Subject, retVal.Message, retVal.CreatedUser.ID, retVal.MessageType)
	if err != nil {
		return retVal, err
	}

	retVal.ID, err = results.LastInsertId()
	if err != nil {
		return retVal, err
	}

	return c.GetByID(retVal.ID)
}

func (c *notificationRepo) getQuery() string {
	return `SELECT
                a.notification_id,
                a.notification_uuid,
                a.ride_id,
                a.user_id,
                c.user_uuid,
								c.name user_name,
								CASE WHEN b.key = 'sms' THEN e.phone_number WHEN b.key = 'email' THEN e.email WHEN b.key = 'app' THEN c.user_uuid END to_address,
								CASE WHEN b.key = 'sms' THEN f.phone_number WHEN b.key = 'email' THEN f.email WHEN b.key = 'app' THEN d.user_uuid END from_address,
								a.contact_type_id,
                b.key contact_type_key,
                a.contact,
                a.message,
                a.create_at,
                a.readed,
                d.user_id created_user_id,
                d.user_uuid created_user_uuid,
								d.name created_user_name,
								a.notificaton_type
  FROM
                tab_notification        a
 INNER JOIN     tab_contact_type        b
    ON          a.contact_type_id       =   b.contact_type_id
 INNER JOIN     tab_user                c
    ON          a.user_id               =   c.user_id 
 INNER JOIN     tab_user                d
		ON          a.created_user_id       =   d.user_id
 INNER JOIN			tab_login								e
		ON					c.user_id								=		e.user_id
 INNER JOIN			tab_login								f
		 ON					d.user_id								=		f.user_id
 INNER JOIN			tab_ride								g
 		 ON					g.ride_id							  =		a.ride_id `
}

func (c *notificationRepo) GetLastNotificationFromPhoneNumber(notificationType string, phoneNumber string, status string) (entity.Notification, error) {
	const (
		whereClause = ` WHERE 
																	 a.notificaton_type  = ?
											AND          e.phone_number      = ?
											AND          b.key               = ?
										ORDER BY       a.notification_id DESC
										LIMIT 1;`
	)
	finalQuery := c.getQuery() + whereClause

	return c.parseEntity(c.conn.QueryRow(finalQuery, notificationType, phoneNumber, status))
}

func (c *notificationRepo) GetByUserUUIDAndReadStatus(userUUID string, status string, isRead bool) ([]entity.Notification, error) {
	return c.parseSet(c.conn.Query(c.getQuery()+" WHERE a.readed = ? AND b.key = ? AND c.user_uuid = ?; ", isRead, status, userUUID))
}

func (c *notificationRepo) GetByUserUUID(userUUID string, status string) ([]entity.Notification, error) {
	return c.parseSet(c.conn.Query(c.getQuery()+" WHERE  b.key = ? AND c.user_uuid = ?; ", status, userUUID))
}

func (c *notificationRepo) ReadStatus(notificationUUID string, isRead bool) error {
	const query = "UPDATE tab_notification SET readed = ? WHERE notification_uuid = ?"
	_, err := c.conn.Exec(query, isRead, notificationUUID)
	return err
}

func (c *notificationRepo) GetByID(ID int64) (entity.Notification, error) {
	return c.parseEntity(c.conn.QueryRow(c.getQuery()+" WHERE  a.notification_id = ?; ", ID))
}

// parseSet parses a result set result to an entity array
func (c *notificationRepo) parseSet(rows *sql.Rows, err error) ([]entity.Notification, error) {
	if err != nil {
		return nil, errors.Wrap(err)
	}
	result := make([]entity.Notification, 0)
	for rows.Next() {
		entity, err := c.parseEntity(rows)
		if err != nil {
			return nil, errors.Wrap(err)
		}
		result = append(result, entity)
	}
	return result, nil
}

// parseEntity parses a result to an entity
func (c *notificationRepo) parseEntity(row scanner) (retVal entity.Notification, err error) {
	err = row.Scan(
		&retVal.ID, &retVal.UUID, &retVal.Ride.ID, &retVal.User.ID, &retVal.User.UUID, &retVal.User.Name, &retVal.To, &retVal.From, &retVal.TypeID, &retVal.Type, &retVal.Subject, &retVal.Message, &retVal.Created, &retVal.Read, &retVal.CreatedUser.ID, &retVal.CreatedUser.UUID, &retVal.CreatedUser.Name, &retVal.MessageType)

	return retVal, errors.Wrap(err)
}
