/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF 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.apache.hadoop.ozone.s3.endpoint;

import javax.ws.rs.core.HttpHeaders;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import org.apache.commons.lang3.StringUtils;
import org.apache.hadoop.ozone.s3.exception.OS3Exception;
import org.apache.hadoop.ozone.s3.exception.S3ErrorTable;
import org.apache.hadoop.ozone.s3.util.S3Consts;

/**
 * Represents an owner of S3 resources in the Ozone S3 compatibility layer.
 *
 * This class models the Owner XML element in S3 responses, containing both a unique ID
 * and a display name for the owner of buckets and objects.
 *
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "Owner")
public class S3Owner {

  // The default S3 owner ID, generated by S3Utils.generateCanonicalUserId with input: "ozone"
  public static final String DEFAULT_S3OWNER_ID = "bb2bd7ca4a327f84e6cd3979f8fa3828a50a08893c1b68f9d6715352c8d07b93";
  public static final S3Owner
      DEFAULT_S3_OWNER = new S3Owner(DEFAULT_S3OWNER_ID, "ozone");

  @XmlElement(name = "DisplayName")
  private String displayName;

  @XmlElement(name = "ID")
  private String id;

  public S3Owner() {

  }

  public static S3Owner of(String displayName) {
    return new S3Owner(DEFAULT_S3OWNER_ID, displayName);
  }

  public S3Owner(String id, String displayName) {
    this.id = id;
    this.displayName = displayName;
  }

  public String getDisplayName() {
    return displayName;
  }

  public void setDisplayName(String name) {
    this.displayName = name;
  }

  public String getId() {
    return id;
  }

  public void setId(String id) {
    this.id = id;
  }

  @Override
  public String toString() {
    return "S3Owner{" +
        "displayName='" + displayName + '\'' +
        ", id='" + id + '\'' +
        '}';
  }

  /**
   * Checks whether the HTTP headers contain a bucket ownership verification conditions,
   * specifically if either the `expected-bucket-owner` or
   * `expected-source-bucket-owner` header is present and not empty.
   *
   * @param headers the HTTP headers to check
   * @return true if either bucket ownership verification condition header is present and not empty, false otherwise
   */
  public static boolean hasBucketOwnershipVerificationConditions(HttpHeaders headers) {
    if (headers == null) {
      return false;
    }
    final String expectedBucketOwner = headers.getHeaderString(S3Consts.EXPECTED_BUCKET_OWNER_HEADER);
    if (!StringUtils.isEmpty(expectedBucketOwner)) {
      return true;
    }
    final String expectedSourceBucketOwner = headers.getHeaderString(S3Consts.EXPECTED_SOURCE_BUCKET_OWNER_HEADER);
    return !StringUtils.isEmpty(expectedSourceBucketOwner);
  }

  /**
   * Verify the bucket owner condition.
   *
   * @param headers       HTTP headers
   * @param bucketName    bucket name
   * @param bucketOwner   bucket owner
   * @throws OS3Exception if the expected bucket owner does not match
   */
  public static void verifyBucketOwnerCondition(HttpHeaders headers, String bucketName, String bucketOwner)
      throws OS3Exception {
    verify(headers, S3Consts.EXPECTED_BUCKET_OWNER_HEADER, bucketOwner, bucketName);
  }

  /**
   * Verify the bucket owner condition on copy operation.
   *
   * @param headers          HTTP headers
   * @param sourceBucketName source bucket name
   * @param sourceOwner      source bucket owner
   * @param destBucketName   dest bucket name
   * @param destOwner        destination bucket owner
   * @throws OS3Exception if the expected source or destination bucket owner does not match
   */
  public static void verifyBucketOwnerConditionOnCopyOperation(HttpHeaders headers, String sourceBucketName,
                                                               String sourceOwner, String destBucketName,
                                                               String destOwner)
      throws OS3Exception {
    verify(headers, S3Consts.EXPECTED_SOURCE_BUCKET_OWNER_HEADER, sourceOwner, sourceBucketName);
    verify(headers, S3Consts.EXPECTED_BUCKET_OWNER_HEADER, destOwner, destBucketName);
  }

  private static void verify(HttpHeaders headers, String headerKey, String actualOwner, String bucketName)
      throws OS3Exception {
    if (headers == null || actualOwner == null) {
      return;
    }
    final String expectedBucketOwner = headers.getHeaderString(headerKey);
    if (StringUtils.isEmpty(expectedBucketOwner)) {
      return;
    }
    if (expectedBucketOwner.equals(actualOwner)) {
      return;
    }
    throw S3ErrorTable.newError(S3ErrorTable.BUCKET_OWNER_MISMATCH, bucketName);
  }
}
