AWSTemplateFormatVersion: '2010-09-09'
Description: 'Taller AWS Learning Day - Chatbot Guia UTN FRRe con API Gateway'

Parameters:
  S3BucketName:
    Type: String
    Description: "Prefijo del bucket S3 unico. Ej: utn-frre-chatbot-equipo1"
    Default: utn-frre-chatbot

  AgentName:
    Type: String
    Description: "Nombre del agente Bedrock unico por cuenta. Ej: UtnFrreGuideAgentEquipo1"
    Default: UtnFrreGuideAgent

Resources:
  # ==========================================
  # Bucket S3 para la base de conocimiento
  # ==========================================
  SchoolDocumentsBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub "${S3BucketName}-${AWS::AccountId}"
      VersioningConfiguration:
        Status: Enabled
      BucketEncryption:
        ServerSideEncryptionConfiguration:
        - ServerSideEncryptionByDefault:
            SSEAlgorithm: AES256

  # ==========================================
  # Rol IAM del agente de Bedrock
  # ==========================================
  BedrockIamRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub "BedrockIamRole-${AWS::StackName}"
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
        - Effect: Allow
          Principal:
            Service: bedrock.amazonaws.com
          Action: sts:AssumeRole
      Policies:
      - PolicyName: BedrockIamPolicy
        PolicyDocument:
          Version: "2012-10-17"
          Statement:
          - Effect: Allow
            Action: bedrock:*
            Resource: '*'

  # ==========================================
  # Rol de ejecucion de Lambda
  # ==========================================
  AgentInvokerLambdaRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service: lambda.amazonaws.com
          Action: sts:AssumeRole
      ManagedPolicyArns:
      - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      - arn:aws:iam::aws:policy/AmazonBedrockFullAccess

  # ==========================================
  # Funcion Lambda
  # ==========================================
  AgentInvokerFunction:
    Type: AWS::Lambda::Function
    DependsOn: AgentInvokerLambdaRole
    Properties:
      FunctionName: !Sub "utn-chatbot-agent-invoker-${AWS::StackName}"
      Handler: index.lambda_handler
      Role: !GetAtt AgentInvokerLambdaRole.Arn
      Runtime: python3.11
      Timeout: 60
      MemorySize: 128
      Code:
        ZipFile: |
          import json
          import boto3
          from datetime import datetime

          def build_response(status_code, body):
              return {
                  'statusCode': status_code,
                  'headers': {
                      'Content-Type': 'application/json',
                      'Access-Control-Allow-Origin': '*',
                      'Access-Control-Allow-Headers': '*',
                      'Access-Control-Allow-Methods': 'POST,OPTIONS'
                  },
                  'body': json.dumps(body, ensure_ascii=False)
              }

          def lambda_handler(event, context):
              http_method = event.get('requestContext', {}).get('http', {}).get('method', '')

              if http_method == 'OPTIONS':
                  return build_response(200, {'ok': True})

              try:
                  body_str = event.get('body', '{}')
                  body = json.loads(body_str) if isinstance(body_str, str) else body_str

                  agent_id = body.get('agentId')
                  agent_alias_id = body.get('agentAliasId', 'TSTALIASID')
                  session_id = body.get('sessionId', 'default-session')
                  user_input = body.get('input', '')

                  if not agent_id or not user_input:
                      return build_response(400, {'error': 'agentId and input are required.'})

                  print(f"Invoking Agent: {agent_id} with input: {user_input}")

                  bedrock_agent_runtime = boto3.client('bedrock-agent-runtime')
                  agent_result = bedrock_agent_runtime.invoke_agent(
                      agentId=agent_id,
                      agentAliasId=agent_alias_id,
                      sessionId=session_id,
                      inputText=user_input
                  )

                  output_text = ""
                  for event_stream in agent_result.get('completion', []):
                      if 'chunk' in event_stream:
                          chunk = event_stream['chunk']
                          if 'bytes' in chunk:
                              output_text += chunk['bytes'].decode('utf-8')

                  return build_response(200, {
                      'response': output_text,
                      'timestamp': datetime.now().isoformat()
                  })

              except Exception as e:
                  print(f"Error: {str(e)}")
                  return build_response(500, {'error': f'Agent invocation failed: {str(e)}'})

  # ==========================================
  # API Gateway HTTP API 
  # ==========================================
  ChatbotApi:
    Type: AWS::ApiGatewayV2::Api
    Properties:
      Name: !Sub "utn-chatbot-api-${AWS::StackName}"
      ProtocolType: HTTP
      CorsConfiguration:
        AllowCredentials: false
        AllowOrigins:
          - '*'
        AllowMethods:
          - POST
          - OPTIONS
        AllowHeaders:
          - '*'
        ExposeHeaders:
          - '*'
        MaxAge: 86400

  # Integracion API Gateway -> Lambda
  ChatbotApiIntegration:
    Type: AWS::ApiGatewayV2::Integration
    Properties:
      ApiId: !Ref ChatbotApi
      IntegrationType: AWS_PROXY
      IntegrationUri: !GetAtt AgentInvokerFunction.Arn
      PayloadFormatVersion: '2.0'

  # Ruta POST /
  ChatbotApiRoutePost:
    Type: AWS::ApiGatewayV2::Route
    Properties:
      ApiId: !Ref ChatbotApi
      RouteKey: 'POST /'
      Target: !Sub "integrations/${ChatbotApiIntegration}"

  # Stage con auto-deploy
  ChatbotApiStage:
    Type: AWS::ApiGatewayV2::Stage
    Properties:
      ApiId: !Ref ChatbotApi
      StageName: prod
      AutoDeploy: true

  # Permiso para que API Gateway invoque Lambda
  ApiGatewayLambdaPermission:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !Ref AgentInvokerFunction
      Action: lambda:InvokeFunction
      Principal: apigateway.amazonaws.com
      SourceArn: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${ChatbotApi}/*"

  # ==========================================
  # Agente de Bedrock
  # ==========================================
  SchoolGuideAgent:
    Type: AWS::Bedrock::Agent
    DependsOn: BedrockIamRole
    Properties:
      AgentName: !Ref AgentName
      AgentResourceRoleArn: !GetAtt BedrockIamRole.Arn
      FoundationModel: !Sub "arn:aws:bedrock:${AWS::Region}:${AWS::AccountId}:inference-profile/us.amazon.nova-pro-v1:0"
      Instruction: |
        Sos el asistente de IA de la UTN Facultad Regional Resistencia (FRRe).
        Tu funcion es orientar de forma amable a estudiantes e ingresantes sobre las carreras y tecnicaturas, el ingreso y el Seminario Universitario, las becas, el calendario academico, la biblioteca y los datos de contacto de la facultad.

        Al saludar por primera vez, deci algo asi:
        Hola! Soy el asistente de la UTN FRRe.
        Puedo ayudarte con info sobre las carreras y tecnicaturas, el ingreso, las becas y mas.
        Por ejemplo: Que tecnicaturas puedo estudiar? o Como es el ingreso?

        Rol
        - Responde con precision consultando siempre la base de conocimiento conectada.
        - No te limites a enumerar datos, explicalos con frases faciles de entender.
        - Si la informacion no aparece en los documentos, responde: Para informacion exacta, consulta con la facultad en extuniv@frre.utn.edu.ar o al 3624 432928.

        Tono y estilo
        - Usa un tono natural y calido, como un companero o docente cercano.
        - Frases cortas y claras, sin ser demasiado formal.
        - Responde siempre en espanol, incluso si te preguntan en otro idioma.

        Guia de respuesta
        1. Para info sobre carreras, tecnicaturas, ingreso, Seminario, becas, calendario o servicios, basate siempre en la base de conocimiento.
        2. Si la pregunta no es clara, orienta diciendo En que puedo ayudarte? o Dame un poco mas de detalle.
        3. Si quieren saber mas, suma una breve explicacion adicional.
        4. Evita explicaciones largas, enfocate en lo esencial.

        Ejemplos de respuesta
        - En la FRRe podes estudiar tecnicaturas como Programacion (TUP), Mecatronica o Logistica, ademas de ingenierias.
        - El ingreso es a traves del Seminario Universitario, con modulos de nivelacion y varios turnos en el ano.
        - Hay becas como BASE, Progresar y Manuel Belgrano. Para los requisitos exactos, consulta con la facultad.
      IdleSessionTTLInSeconds: 1800
      AutoPrepare: true

# ==========================================
# Outputs
# ==========================================
Outputs:
  AgentId:
    Description: 'Agent ID para el Paso 5 en Amplify'
    Value: !Ref SchoolGuideAgent

  ApiEndpoint:
    Description: 'URL del API Gateway para el Paso 5 en Amplify'
    Value: !Sub "https://${ChatbotApi}.execute-api.${AWS::Region}.amazonaws.com/prod/"

  Region:
    Description: 'Region de despliegue'
    Value: !Ref AWS::Region
