/*
 * Licensed to Elasticsearch under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch licenses this file to you under
 * the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.elasticsearch.client.watcher;

import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.XContentTestUtils;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;

import java.io.IOException;
import java.util.function.Predicate;

public class WatchStatusTests extends ESTestCase {

    public void testBasicParsing() throws IOException {
        int expectedVersion = randomIntBetween(0, 100);
        ExecutionState expectedExecutionState = randomFrom(ExecutionState.values());
        boolean expectedActive = randomBoolean();
        ActionStatus.AckStatus.State expectedAckState = randomFrom(ActionStatus.AckStatus.State.values());

        XContentBuilder builder = createTestXContent(expectedVersion, expectedExecutionState,
            expectedActive, expectedAckState);
        BytesReference bytes = BytesReference.bytes(builder);

        WatchStatus watchStatus = parse(builder.contentType(), bytes);

        assertEquals(expectedVersion, watchStatus.version());
        assertEquals(expectedExecutionState, watchStatus.getExecutionState());

        assertEquals(new DateTime(1432663467763L, DateTimeZone.UTC), watchStatus.lastChecked());
        assertEquals(DateTime.parse("2015-05-26T18:04:27.763Z"), watchStatus.lastMetCondition());

        WatchStatus.State watchState = watchStatus.state();
        assertEquals(expectedActive, watchState.isActive());
        assertEquals(DateTime.parse("2015-05-26T18:04:27.723Z"), watchState.getTimestamp());

        ActionStatus actionStatus = watchStatus.actionStatus("test_index");
        assertNotNull(actionStatus);

        ActionStatus.AckStatus ackStatus = actionStatus.ackStatus();
        assertEquals(DateTime.parse("2015-05-26T18:04:27.763Z"), ackStatus.timestamp());
        assertEquals(expectedAckState, ackStatus.state());

        ActionStatus.Execution lastExecution = actionStatus.lastExecution();
        assertEquals(DateTime.parse("2015-05-25T18:04:27.733Z"), lastExecution.timestamp());
        assertFalse(lastExecution.successful());
        assertEquals("failed to send email", lastExecution.reason());

        ActionStatus.Execution lastSuccessfulExecution = actionStatus.lastSuccessfulExecution();
        assertEquals(DateTime.parse("2015-05-25T18:04:27.773Z"), lastSuccessfulExecution.timestamp());
        assertTrue(lastSuccessfulExecution.successful());
        assertNull(lastSuccessfulExecution.reason());

        ActionStatus.Throttle lastThrottle = actionStatus.lastThrottle();
        assertEquals(DateTime.parse("2015-04-25T18:05:23.445Z"), lastThrottle.timestamp());
        assertEquals("throttling interval is set to [5 seconds] ...", lastThrottle.reason());
    }

    public void testParsingWithUnknownKeys() throws IOException {
        int expectedVersion = randomIntBetween(0, 100);
        ExecutionState expectedExecutionState = randomFrom(ExecutionState.values());
        boolean expectedActive = randomBoolean();
        ActionStatus.AckStatus.State expectedAckState = randomFrom(ActionStatus.AckStatus.State.values());

        XContentBuilder builder = createTestXContent(expectedVersion, expectedExecutionState,
            expectedActive, expectedAckState);
        BytesReference bytes = BytesReference.bytes(builder);

        Predicate<String> excludeFilter = field -> field.equals("actions");
        BytesReference bytesWithRandomFields = XContentTestUtils.insertRandomFields(
            builder.contentType(), bytes, excludeFilter, random());

        WatchStatus watchStatus = parse(builder.contentType(), bytesWithRandomFields);

        assertEquals(expectedVersion, watchStatus.version());
        assertEquals(expectedExecutionState, watchStatus.getExecutionState());
    }

    public void testOptionalFieldsParsing() throws IOException {
        XContentType contentType = randomFrom(XContentType.values());
        XContentBuilder builder = XContentFactory.contentBuilder(contentType).startObject()
            .field("version", 42)
            .startObject("actions")
                .startObject("test_index")
                    .startObject("ack")
                        .field("timestamp", "2015-05-26T18:04:27.763Z")
                        .field("state", "ackable")
                    .endObject()
                    .startObject("last_execution")
                        .field("timestamp", "2015-05-25T18:04:27.733Z")
                        .field("successful", false)
                        .field("reason", "failed to send email")
                    .endObject()
                .endObject()
            .endObject()
        .endObject();
        BytesReference bytes = BytesReference.bytes(builder);

        WatchStatus watchStatus = parse(builder.contentType(), bytes);

        assertEquals(42, watchStatus.version());
        assertNull(watchStatus.getExecutionState());
        assertFalse(watchStatus.checked());
    }

    private XContentBuilder createTestXContent(int version,
                                               ExecutionState executionState,
                                               boolean active,
                                               ActionStatus.AckStatus.State ackState) throws IOException {
        XContentType contentType = randomFrom(XContentType.values());
        return XContentFactory.contentBuilder(contentType).startObject()
            .field("version", version)
            .field("execution_state", executionState)
            .field("last_checked", 1432663467763L)
            .field("last_met_condition", "2015-05-26T18:04:27.763Z")
            .startObject("state")
                .field("active", active)
                .field("timestamp", "2015-05-26T18:04:27.723Z")
            .endObject()
            .startObject("actions")
                .startObject("test_index")
                    .startObject("ack")
                        .field("timestamp", "2015-05-26T18:04:27.763Z")
                        .field("state", ackState)
                    .endObject()
                    .startObject("last_execution")
                        .field("timestamp", "2015-05-25T18:04:27.733Z")
                        .field("successful", false)
                        .field("reason", "failed to send email")
                    .endObject()
                    .startObject("last_successful_execution")
                        .field("timestamp", "2015-05-25T18:04:27.773Z")
                        .field("successful", true)
                    .endObject()
                    .startObject("last_throttle")
                        .field("timestamp", "2015-04-25T18:05:23.445Z")
                        .field("reason", "throttling interval is set to [5 seconds] ...")
                    .endObject()
                .endObject()
            .endObject()
        .endObject();
    }

    private WatchStatus parse(XContentType contentType, BytesReference bytes) throws IOException {
        XContentParser parser = XContentFactory.xContent(contentType)
            .createParser(NamedXContentRegistry.EMPTY, null, bytes.streamInput());
        parser.nextToken();

        return WatchStatus.parse(parser);
    }
}
